Skip to content

设计理念

四个原则贯穿整个项目。

🎯大而全
每一个能被封装的 crt.sh 能力,都必须被封装
🔗四层同源
SDK 是唯一事实来源,上层都是薄包装
🪞忠实映射
不发明抽象,crt.sh 的 URL 语义原样复刻
🛡️强类型可观测
typed error + 前置校验 + debug + 重试

1. 大而全

铁律

每一个 crt.sh 能被封装的能力,都必须被封装。

crt.sh 的每个 URL 端点都有对应的 SDK 方法、MCP 工具、CLI 命令,不留"半截子":

flowchart LR subgraph crtsh端点["crt.sh 端点"] E1["?id="] E2["?d="] E3["?asn1="] E4["?h="] E5["?graph="] E6["?pv="] E7["?ctid="] E8["/atom"] E9["/csv"] E10["/cert-populations"] E11["ca?id="] E12["?caid="] E13["?a=1"] E14["13 个 info 页"] end crtsh端点 --> M["每个端点 → 1 个 SDK 方法<
>→ 1 个 MCP 工具 → 1 个 CLI 命令"]

2. 四层同源

SDK 是唯一的事实来源。MCP 工具、CLI 命令、Skills 描述的能力,最终都落到 pkg/crtsh 的方法上。

flowchart LR SDK["pkg/crtsh 方法<
>(唯一事实来源)"] MCP["MCP 工具"] --> SDK CLI["CLI 命令"] --> SDK SKILL["Skills 描述"] --> MCP SKILL --> SDK

新增能力的固定流程

SDK 加方法 → MCP 加工具 → CLI 加命令 → Skills 加描述CLAUDE.md 把这条流程写成了铁律,确保四层永远同步。

3. 忠实映射 crt.sh 的 URL 语义

不发明抽象。 crt.sh 的 JS 怎么构造 URL,SDK 就怎么构造。这是 buildQuery 函数的使命——它是 crt.sh 前端 JS 的 Go 翻译:

🔑搜索类型即参数名
?CN=example.com,不是 ?searchtype=CN&common_name=...
布尔值用 crt.sh 格式
exclude=expireddeduplicate=Y,不是 =on
🔧linter 名作参数名
?zlint=issues,不是 ?linter=zlint&...
🏛️issuer CA ID 过滤
?iCAID=<id>,与 crt.sh 表格链接一致

避免的陷阱

"封装层自创一套参数语义、与 crt.sh 实际行为脱节"——本项目刻意避开。看 buildQuery 就是 crt.sh 前端 JS 的 Go 翻译。

4. 强类型与可观测

flowchart TB subgraph 强类型["强类型错误"] N["NotFound — 没找到"] RL["RateLimit — 被限流"] SV["Server — 5xx"] PR["Parse — 解析失败"] IV["Invalid — 输入非法"] end N --> J["IsNotFoundError()"] RL --> J2["IsRateLimitError()"] SV --> J3["IsServerError()"] PR --> J4["IsParseError()"] IV --> J5["IsInvalidError()"]

调用方可对不同错误做不同处理:

go
switch {
case crtsh.IsNotFoundError(err):
    // 没找到 → 换思路
case crtsh.IsRateLimitError(err):
    // 限流 → 退避重试
case crtsh.IsServerError(err):
    // 5xx → 重试
case crtsh.IsParseError(err):
    // 解析失败 → 报告
case crtsh.IsInvalidError(err):
    // 输入非法 → 修正参数
}
前置校验
QueryParams.Validate() 在发请求前就拦下非法搜索类型/匹配模式
🐛可观测
WithDebug(true) 打印请求/响应原文
⏱️可调
WithTimeout / WithRetryCount / WithUserAgent / WithBaseURL
🔁健壮重试
5xx 与网络错误指数退避(500ms × 2^attempt),尊重 ctx.Done()

下一站

深入看 四层架构总览