# moon-dns-stub 总体架构

## 1. 架构目标与约束

架构目标：以单个 Native 公共库 package 拥有 DNS 公共类型和 API，把 wire、EDNS、transport、
resolution/cache、presentation 作为同 package 内的职责组件，再由三个 executable package
消费根库。

约束：

- 不把文件名当作 namespace；`src/*.mbt` 共同组成 `jinshengmeng46/moon-dns-stub`。
- `Header`、`Message`、`RR`、`RData`、`OptRR`、`Resolver` 等用户类型由根公共包拥有。
- 网络依赖只在 Native target 使用；不对 JS/Wasm 声称可用。
- codec、transport、resolver 的错误边界必须能被调用者观察，不以 panic 表示普通失败。
- `.mbti` 只由 `moon info` 生成。
- 同一 package 内不存在编译级 import 边，但实现应保持单向职责：底层 codec 不依赖
  resolver 或 CLI。
- `src/moon.pkg` 和三个 executable package 均 `supported_targets = "native"`。

## 2. 目录结构

```text
moon-dns-stub/
├── moon.mod
├── README.md / LICENSE
├── src/
│   ├── moon.pkg                       # Native 公共根库 package
│   ├── constants.mbt ... message.mbt  # wire codec
│   ├── edns.mbt / edns_options.mbt    # EDNS
│   ├── trait.mbt / udp.mbt /
│   │   tcp.mbt / fallback.mbt         # transport
│   ├── resolver*.mbt / cname.mbt /
│   │   cache.mbt                      # resolution/cache
│   ├── dns_text.mbt / pretty_print.mbt # presentation
│   ├── *_wbtest.mbt                   # 根包白盒与集成测试
│   ├── cmd/main/                      # CLI package
│   └── examples/
│       ├── a_aaaa/                    # A/AAAA example package
│       └── mx_srv/                    # MX/SRV example package
├── test/interop/dig_cases.txt
├── scripts/                           # failure smoke 与 dig/DoH 差分
└── .github/workflows/                 # CI 计划
```

| 组件 | Package/组件 | 职责 | 依赖 |
|---|---|---|---|
| wire-codec | 根包 / wire-codec | DNS 基本类型、名称、message、RR/RDATA 编解码与资源边界 | core |
| edns | 根包 / edns | OPT、DO、扩展 rcode 与 option 校验 | wire-codec |
| transport | 根包 / transport | transport 注入、server 解析、UDP/TCP 和 fallback | wire-codec、async |
| resolver-cache | 根包 / resolver-cache | 查询状态、响应校验、CNAME、typed API、TTL/LRU cache | wire-codec..transport |
| presentation | 根包 / presentation | qtype/rcode/RR/result 的 short、verbose、JSON 表达 | wire-codec、resolver-cache |
| cli | `src/cmd/main` | CLI 参数、执行、诊断和退出码 | 根公共包、env、async、sys |
| example-a-aaaa | `src/examples/a_aaaa` | 地址查询最小示例 | 根公共包、env、async、sys |
| example-mx-srv | `src/examples/mx_srv` | 邮件/服务发现示例 | 根公共包、env、async、sys |

## 3. 核心类型与公开接口

公开类型所有权全部在根公共包：

- Wire：`Header`、`Question`、`Message`、`RR`、`RData`。
- EDNS：`OptRR`、`OptOption`。
- Transport：`DnsTransport`、`UdpTransport`、`TcpTransport`、
  `FallbackTransport`、`TransportError`、`ServerAddress`。
- Resolution：`Resolver`、`ResolveOptions`、`ResolveError`、`DnsResult`、
  `CnameTracker`、`DnsCache`、`CacheLookup`、`CacheStats`、`SoaResult`、`SrvResult`。

主要入口：

```moonbit
pub fn decode_message(Array[Byte]) -> Result[Message, String]
pub fn Message::encode_checked(Message) -> Result[Array[Byte], String]
pub fn DnsTransport::from_request(
  async (Array[Byte]) -> Result[Array[Byte], TransportError],
  udp_payload_size? : UInt16,
) -> DnsTransport
pub fn Resolver::new(options? : ResolveOptions) -> Resolver
pub fn Resolver::with_transport(
  transport~ : DnsTransport,
  options? : ResolveOptions,
) -> Resolver
pub async fn Resolver::resolve(
  Resolver,
  String,
  UInt16,
) -> Result[DnsResult, ResolveError]
```

`Array` 字段和返回值按 MoonBit 引用语义使用。对外缓存结果必须深拷贝或明确不允许调用者修改
内部状态；该不变量由 cache 测试验证。DNS 名称相等性使用原始 label 的 ASCII
case-insensitive 规范化，而不是 presentation string 的简单比较。

## 4. 数据流与关键算法

```text
name/qtype/options
  → checked name + EDNS query encode
  → DnsTransport.send
      → UDP timeout/retry
      → TC=1 ? TCP length-prefix exact read : UDP response
  → decode_message
  → validate ID / QR / opcode / question / rcode
  → follow in-message or cross-query CNAME with visited/depth bound
  → typed record extraction
  → positive or SOA-derived negative TTL/LRU cache
  → DnsResult / typed API / presentation
```

名称压缩 encoder 维护 canonical suffix→offset；decoder 限制 pointer hops 并保持原始 label
octet 可逆。解析复杂度应近似报文长度加记录数；跳转、section、option 和报文大小上限阻止
恶意输入造成无界工作。缓存 lookup/put 以 canonical name+qtype 为 key，LRU 驱逐在容量边界
发生；deadline 使用 Int64 单调毫秒并饱和处理上界。

## 5. 错误模型

- Wire/EDNS checked API：`Result[..., String]`，字符串必须包含可定位的格式原因。
- Transport：`TransportError` 区分 timeout、truncated、连接、发送、接收、server 与 message。
- Resolver：`ResolveError` 区分 format、response mismatch、rcode、NXDOMAIN、NODATA、
  referral、randomness、transport 和 CNAME loop。
- CLI/example：将上述错误转成面向用户的 `error:`，失败状态为 2；help/version 为 0。
- 非 checked convenience encoder 保留兼容性，但 resolver 和所有不可信输入路径必须调用
  checked API。

## 6. 并发与一致性模型

公开查询为 async；单次查询使用局部 tracker，并通过 transport 等待 I/O。`Resolver` 内持有
可变 cache，未设计锁或跨线程同步。本版本不声明同一 resolver 实例可被并发任务安全共享；
调用方应串行调用、在外部协调，或为独立并发流使用不同实例。`DnsTransport` 回调也必须遵守
调用方自己的资源与取消规则。

缓存一致性以单调 clock 的 lookup 时刻为准；命中返回剩余 TTL 快照。CNAME cache 保存相对
provenance，每次顶层查询重放 chain，避免跨 key 污染。

## 7. Target 与互操作边界

- `src/moon.pkg` 和三个 executable package 均 `supported_targets = "native"`。
- Native async socket/TLS 是默认 transport 和 query ID 随机源的实现依赖。
- 自定义 transport 仍然是 Native package 内的注入点，不形成 JS/Wasm 可移植承诺。
- `dig`/Python/DoH 只存在于验证边界，不链接或打包到库中。
- RFC 互操作是有限测试证据，不表述为正式 DNS 规范认证。

## 8. 设计决策记录

| ADR | 决策 | 备选 | 理由 | 代价/重审条件 |
|---|---|---|---|---|
| `ADR-001` | `0.1.0` 使用单一 Native 根库 package | 立即拆 portable/core 与 native adapter | 保持现有 API 与工程闭环，避免无证据跨 target | 未来需要 JS/Wasm 时必须拆包并独立测试 |
| `ADR-002` | 公共 DNS 类型由根包直接拥有 | 放入 `internal/*` 后 re-export | 构造、匹配、方法与 `.mbti` 所有权清楚 | 根包 API 面较大，后续需 SemVer 管理 |
| `ADR-003` | resolver 依赖完整 wire transport 回调 | 暴露 socket-only trait | mock、代理和自定义路径无需侵入 resolver | adapter 自己负责连接、重试和安全 |
| `ADR-004` | NXDOMAIN/NODATA 为不同错误与 cache kind | 空数组表示无结果 | 调用者可区分域不存在与记录不存在 | API 使用者必须显式处理 |
| `ADR-005` | 名称 presentation 采用可逆 `\DDD` | 仅 UTF-8/点分字符串 | 保持任意合法 label octet 的 wire 语义 | UI 展示比简单字符串复杂 |
| `ADR-006` | 不声明 resolver 实例并发安全 | 内部加锁 | 当前闭环无需锁，避免未经验证语义 | 出现共享并发用户需求时重审并加压力测试 |
