深色模式
原子知识单元的判定标准、frontmatter 字段与引用 ID 格式。
本页描述站点已实现的机制。字段表中未列出的字段(如 level、 status、type、除 requires 外的关系边)目前不被 kb-scan 读取,写入 frontmatter 不产生任何效果。
配套文档:分类体系 定义 27 个主题域与簇,本文定义原子单元在这些簇上的标注方式。 写作流程与全部 frontmatter 字段见 使用指南。
0. 当前设计:标注而非独立文件
原子知识单元不是单独的文件,而是叶子目录 index.md 上的一个可选标注。 作者认为某篇文章满足原子标准时,在 frontmatter 里加 atomic: true 与配套字段即可。
这个选择是刻意的。独立的原子文件体系需要 ID 唯一性校验、边指向校验、DAG 检查、 以及一套把原子重新聚合成可读页面的呈现层——在单人维护的规模下, 这些机制的维护成本高于它们消除的冗余。
因此:
| 由系统保证 | 由作者判断 |
|---|---|
| 有标注的页面渲染出元数据面板 | 这篇是否值得标为原子 |
| 父页表格显示 ⚛ 徽章 | claim 是否真的只有一个断言 |
字段类型容错(非数组的 keywords 忽略而非报错) | requires 里的 ID 是否存在 |
没有校验脚本。 requires 里写错的 ID 不会导致构建失败,也不会有警告—— 它只是不会指向任何东西。这是用可靠性换取灵活性的取舍:宁可某处引用失准, 也不要因为一个笔误阻断整站构建。
1. 原子的边界
一个原子 = 一个可独立判断对错的断言,加上让它站得住的最小上下文。
判断粒度是否合适,用三条测试:
| 测试 | 通过标准 | 不通过说明 |
|---|---|---|
| 单断言测试 | claim 能用一句话写完,不用「并且」 | 太大,拆分 |
| 独立检验测试 | 能设计出一个明确的验证动作 | 太虚,要么细化要么并入邻近条目 |
| 复用测试 | 至少能想到两处会引用它,或它本身是终点应用 | 只被一处用到且很短,并入那处 |
第一条测试是最有效的:claim 字段强制你把知识点压成一句话, 压不动就说明这个目录里装了多件事,应该拆成子目录。
1.1 粒度示例
以「栈溢出利用」为例,正确的拆分:
| 目录 | claim(一句话) |
|---|---|
vuln/memory/stack-layout/ | 函数栈帧中返回地址位于局部变量高地址侧,越界写可覆盖它 |
vuln/memory/ret2text/ | 覆盖返回地址为程序内已有代码地址即可劫持控制流 |
vuln/mitigation/stack-canary/ | 在返回地址前放置随机值并在返回前校验,可检测线性覆写 |
vuln/bypass/canary-leak/ | 通过信息泄露读出 canary 值后原样写回,可绕过校验 |
错误的拆分(三种典型):
- 太粗:一个
stack-overflow/目录讲完布局、利用、防护、绕过 →claim写不成一句话,且无法单独标记完成度 - 太细:把「返回地址是 8 字节」单独做一个目录 → 通不过复用测试,应作为
stack-layout的正文细节 - 切错维度:按工具切成
gdb-调栈溢出//pwntools-调栈溢出/→ 工具是正文里的操作细节,不是知识点的划分维度
1.2 一个原子不该包含什么
- 多个平台的做法。Windows 与 Linux 的同类机制应是两个目录,在正文里互相链接
- 教程式的完整流程。流程放非叶子索引页的手写导语里,原子只放流程中的一个环节
- 背景介绍。历史沿革、行业现状放上级索引页
2. frontmatter 字段
以下是 kb-scan 实际读取的全部原子相关字段:
yaml
---
title: 长度扩展攻击 # 必填(所有 kb 页面通用)
summary: Merkle-Damgård 的可延长性 # 可选,父页表格说明列
order: 4 # 可选,兄弟排序
complete: true # 可选,计入父页进度
atomic: true # 原子面板的唯一开关
claim: > # 一句话断言
对 Merkle-Damgård 结构的哈希,已知 H(m) 与 len(m)
即可计算 H(m‖pad‖ext) 而无需知道 m。
keywords: # 别名、缩写、工具名、报错文本
- length extension
- 长度扩展
- hashpump
- secret-prefix MAC
requires: # 前置知识的原子 ID
- crypto.hash-mac.merkle-damgard
---| 字段 | 类型 | 缺省行为 | 效果 |
|---|---|---|---|
atomic | boolean | false | 唯一触发条件。非 true 时其余三个字段全部不生效 |
claim | string | 空 | 面板「核心断言」行;空则该行不渲染 |
keywords | string[] | [] | 面板「关键词」标签;非数组值按空数组处理 |
requires | string[] | [] | 面板「前置知识」ID 列表;仅展示,不生成链接 |
三个内容字段全部可选。只写 atomic: true 会得到一个只有标题行的面板—— 可用,但价值有限,建议至少写 claim。
2.1 claim 的写法
claim 不是摘要,是断言。区别在于断言可以判断对错:
| 写法 | 判断 |
|---|---|
| ✗ 「介绍长度扩展攻击的原理与防护」 | 这是摘要,描述文章而非知识 |
| ✗ 「长度扩展攻击很重要,需要理解」 | 无法判断对错 |
| ✓ 「已知 H(m) 与 len(m) 即可计算 H(m‖pad‖ext) 而无需知道 m」 | 可判断,可验证 |
用 YAML 折叠标量 > 写多行,避免为了塞进一行而牺牲准确性。
2.2 keywords 的取材
keywords 的唯一用途是半年后自己能搜到。因此收录的应是「记不清标题时会输入什么」:
- 英文原名与中文译名各一份
- 缩写(
HMAC、ROP、TSO) - 相关工具名(
hashpump、gdb) - 典型报错文本(
cannot borrow as mutable)——这是命中率最高的一类
建议至少 3 项。搜不到时应该补 keywords,而不是新建一个目录。
2.3 requires 的现状
requires 目前只在面板里以 code 样式展示 ID 文本,不解析、不校验、不生成链接。 它的价值是记录你写作时的判断:这篇的读者需要先懂什么。
写了不存在的 ID 不会报错,但也不会有任何提示。若要确保 ID 准确, 写入前手工确认目标目录存在。
3. 引用 ID 格式
<域>.<簇>.<slug>
↓ ↓ ↓
vuln . heap . tcache-poisoning固定三段。注意大类目录层不参与 ID——kb/security/vuln/heap/ 的 ID 是 vuln.heap.tcache-poisoning,不是 security.vuln.heap.tcache-poisoning。 大类只决定 URL 与导航分组,不进 ID。
3.1 约定
| 规则 | 理由 |
|---|---|
| 一旦在别处引用就不改 | ID 是 requires 的锚点,改了引用方不会有任何提示 |
| 不含数字编号 | 编号隐含顺序,重排即失效 |
| 全小写,连字符分词 | 与 URL 一致 |
| slug 描述内容不描述形式 | tcache-poisoning 而非 tcache-详解 |
| 同簇内唯一即可 | 域与簇已提供命名空间 |
这些是约定,不是校验。没有脚本检查 ID 唯一性或格式合法性。
3.2 slug 与目录名的关系
第三段 slug 就是叶子目录名。所以给目录起名时同时是在定 ID:
docs/kb/security/vuln/heap/tcache-poisoning/index.md
└──┘ └──┘ └───────────────┘
域 簇 slug
↓
ID = vuln.heap.tcache-poisoning目录名用小写连字符也是出于这个原因:目录名直接进 ID,也直接进 URL。
3.3 域短码清单
与 分类体系 严格一致,共 27 个:
math logic algo
arch compiler lang
os net comm
crypto priv
re protect vuln malware offsec defsec ctf
ml agent aiinfra
be web sysdev ops
model web3注意 lang 域的簇是语言名,所以 Rust 所有权的 ID 是 lang.rust.ownership-move—— 域短码只有 lang,没有 lang-rust 这种写法。
4. 与 complete 的关系
atomic 与 complete 是两个独立维度,互不影响:
atomic | complete | |
|---|---|---|
| 语义 | 这篇是不是一个单一知识点 | 这篇写完了没有 |
| 生效范围 | 任意页面(实践上只用于叶子) | 仅叶子,非叶子的该字段被忽略 |
| 影响 | 渲染元数据面板、父页 ⚛ 徽章 | 计入父页 done/total 进度、父页 ✓ 标记 |
一篇文章可以是 atomic: true 且 complete: false(原子边界已定,内容待填), 也可以 complete: true 而不标 atomic(写完了但不是单一知识点,比如一个簇的综述)。
4.1 叶子变非叶子时的语义变化
在一个已标 complete: true 的叶子下新建子目录,它就不再是叶子:
complete: true随即被忽略,该目录的完成度改由后代递归得出atomic: true仍然生效——面板照常渲染。但这通常是个信号: 一个需要往下细分的目录,很可能已经不是单一知识点了,应重新过一遍第 1 节的三条测试- 需要手工补
tree: true,否则该页不会出现子目录表格
第二条是唯一需要人工介入的地方。系统不会提醒,因为「细分后仍是原子」在 少数情况下是合理的(例如原子本体加若干平台实现的分页)。
5. 不做什么
明确记录被否决的方案,避免日后重复讨论:
| 方案 | 否决理由 |
|---|---|
独立的 atoms/ 原子文件目录 | 与 kb/ 树形成两套结构,必须手工同步 |
十种关系边(attacks/mitigates/refines…) | 单人维护下填不满,填不满的边比没有边更误导 |
level / status 分级字段 | complete 已够用;细分等级需要全局校准才有意义 |
| 掌握度加权统计 | 输入数据(level/status)本身不可靠,算出来的数也不可靠 |
bundle / track 学习路径文件 | 非叶子索引页的手写导语已能承担这个职责 |
| ID 唯一性与边指向校验脚本 | 校验失败阻断构建的代价,高于引用偶尔失准的代价 |
若某项日后确有需要,新增即可——atomic: true 的现有页面不会因为增加字段而失效, 因为 kb-scan 对未知字段一律忽略。