# tools — 签名端点分发 / 多镜像发布（离线 Go CLI）

零外部依赖（纯标准库），独立 Go module（不污染 `server/`）。

```bash
cd infra/domains/tools
go test ./...     # 单测：签名/验签往返、篡改拒、防回滚、key_id 轮换、镜像一致性/故障转移
go build ./...
```

## endpoint-signer（离线使用）

在**离线机器**上签发文档。私钥永不离开离线机；只分发产出的 `*.v1.json` 和（内置进客户端的）公钥。

```bash
# 1. 生成离线密钥对（私钥 0600，离线两地保存；公钥内置客户端）
go run ./cmd/endpoint-signer keygen -out-prefix ./key -key-id ed25519-2026q2

# 2. 签端点文档（version 单调递增；可用 -prev 自动 +1）
go run ./cmd/endpoint-signer sign -type endpoints \
  -in ../examples/endpoints.payload.example.json \
  -key ./key.ed25519.key -key-id ed25519-2026q2 -version 1 \
  -out endpoints.v1.json

# 3. 签公告文档（/v1/notices 的静态镜像版）
go run ./cmd/endpoint-signer sign -type notices \
  -in ../examples/notices.payload.example.json \
  -key ./key.ed25519.key -key-id ed25519-2026q2 -version 1 \
  -out notices.v1.json

# 4. 验签（含防回滚 -min-version 与 key 轮换多公钥）
go run ./cmd/endpoint-signer verify -in endpoints.v1.json \
  -keys "ed25519-2026q2=<base64pub>[,ed25519-2026q1=<旧公钥>]" \
  -type endpoints -min-version 0

go run ./cmd/endpoint-signer inspect -in endpoints.v1.json
```

文档结构与验签规则见 `../CLIENT-CONTRACT.md`。

## publish-mirrors

把签名文档推到 ≥3 镜像，校验各镜像内容 hash 一致；并能像客户端一样做故障转移取回。

```bash
# 发布 + 一致性校验（任一镜像写失败或内容不一致即非零退出）
go run ./cmd/publish-mirrors publish -in endpoints.v1.json \
  -config ../examples/mirrors.example.json -name endpoints.v1.json

# 只校验（不写）
go run ./cmd/publish-mirrors publish -in endpoints.v1.json \
  -config ... -name endpoints.v1.json -verify-only

# 故障转移取回（单镜像失效仍能从其余镜像取到；可带 -keys 顺带验签）
go run ./cmd/publish-mirrors fetch -config ... -name endpoints.v1.json \
  -keys "ed25519-2026q2=<base64pub>" -type endpoints -out got.json
```

镜像配置（`mirrors.example.json`）：每个镜像有本地内容根 `dir`（发布即写入），可选 `fetch_url`（`file://`/`http(s)://`，用于校验与取回），以及 `sync_cmd`（如 wrangler / aws s3，**仅登记，本工具不执行**——由部署把 `dir` 同步到对应平台）。

## 安全约束

- 私钥（`*.ed25519.key`）、`_publish/`、签名产物默认 gitignore；本目录不入库任何私钥。
- 公钥可入库 / 内置客户端。
- token、身份信息一律不出现在本目录。
