# 资料包格式

> .crib 文件里有什么、怎样签名和校验、可信根与密钥策略，以及发布目录的布局、不可变规则和 HTTP 缓存要求。

---

LLMS 索引： [llms.txt](/llms.txt)

---

> [!NOTE]
>
> 现在没有公开的资料包，也没有正式签名密钥。发布目录规划在 `https://repo.pigsty.io/doc/`，还没有上传。
> 本页描述开发构建和构建工具 `crib-pack` 实现的格式。

## 文件 {#files}

资料包是一个 ZIP 文件（支持 ZIP64，条目为 stored 或 DEFLATE），条目按名称排序，时间戳固定。
2026-11-06 起新发布的资料包以 `.crib` 结尾；之前签发的目录和归档使用 `.pgdocpack`，这些已签名的路径继续有效。
版本目录里的归档路径只接受这两种结尾，`.CRIB`、`.crib.zip` 等都会被拒绝。

App 还没有注册 `.crib` 文件类型，双击不能导入；请用资料库的“导入资料包…”。

一个手册资料包的内容：

```filetree {title="pg/18.6-r4.crib" wrap=true}
- manifest.json   # 清单：身份、版本、语言、文件列表与每个文件的哈希
- manifest.sig    # 对 manifest.json 原始字节的 Ed25519 签名
- sources.json    # 来源：上游修订、版次、修复记录和处理说明
- content.sqlite  # 文档、章节、精确名称键、双语对应、链接和搜索词元
- documents/
  - en/           # 英文 HTML 与资源
  - zh-Hans/      # 简体中文 HTML 与资源
- licenses/       # 许可文本，按语言分目录
```

Wiki 资料包的页面在 `documents/<语言>/wiki/<集合>/` 下，另外带有原始来源记录和许可说明 `licenses/center-sources.json`。

## 清单 {#manifest}

| 字段 | 说明 |
| --- | --- |
| `format_version` | 资料包格式，现在是 `1` |
| `schema_version` | `content.sqlite` 的结构版本，现在是 `1` |
| `analyzer_id` | 分词器标识 `pgdash-han2-v1`；旧名称属于格式身份，不随产品改名 |
| `pack_id` | 资料包身份，如 `postgresql/manual/18`、`postgresql/wiki` |
| `project_id`、`branch`、`content_version`、`channel` | 项目、版本线、内容版本、发布渠道 |
| `pack_revision` | 修订号，同一 `pack_id` 跨内容版本单调递增 |
| `languages` | `["en", "zh-Hans"]` |
| `min_reader_version` | 能读取它的最低 Crib 版本 |
| `source_manifest_sha256` | `sources.json` 的哈希 |
| `files` | 除清单和签名外每个文件的 `path`、`bytes`、`sha256` |
| `licenses` | 许可文件路径 |
| `trust` | 签名类别；开发构建中是 `development` |
{.fields}

清单之外出现未声明的文件，或任何文件与清单哈希不符，资料包都会被拒绝。
Crib 用 `manifest.json` 原始字节的 SHA-256 标识一个已安装的资料包，[链接](/docs/integrations/deep-links/)里的 `manifest` 参数就是这个值。

## 签名与可信根 {#trust}

`manifest.sig` 是 `{"key_id": "…", "signature": "<base64>"}`，签名对象是 `manifest.json` 的原始字节。
目录 `catalog.json` 用同样的 Ed25519 签名。

公钥只来自 App 自带的 `trust.json`。资料包或服务器里的密钥都不会被信任。

```json
{
  "development": false,
  "keys": { "<key_id>": "<base64 公钥>" },
  "key_policies": {
    "<key_id>": { "roles": ["pack", "catalog"], "not_after": "2027-12-31T00:00:00Z" }
  }
}
```

- `key_policies` 可选。`roles` 取 `pack`、`seed`、`catalog` 的子集；`not_after` 必须是 `YYYY-MM-DDTHH:MM:SSZ`。没有列出的密钥可签所有用途、永不过期。
- 未知字段、空的 `roles`、不规范的日期、指向不存在密钥的策略都会让整个可信根被拒绝。
- 密钥过期只阻止获取（刷新目录、下载、导入）和发布端签名，已安装的内容照常可用。
- 从可信根删除密钥等于吊销。要退役一把密钥，先设 `not_after`。

开发签名的内容只在两种情况下被接受：构建在 `Info.plist` 中声明 `CribDevelopmentContent=true`，或测试环境设置
`CRIB_ALLOW_DEVELOPMENT_CONTENT=1`。这时资料库显示[“非正式发布”](/docs/library/manage/#development-content)。

## 导入与下载时的检查 {#checks}

解包之前，Crib 先完整检查整个 ZIP，拒绝符号链接、特殊文件、加密条目、分卷、路径穿越、大小写或 Unicode 规范化后重名的路径、多余的尾部字节。

| 上限 | 值 |
| --- | --- |
| 条目数 | 65,000 |
| 单个文件 | 8 GiB |
| 解包后总大小 | 16 GiB |
| 压缩文件 | 20 GiB |
| 压缩比 | 1000:1 |

然后核对签名、每个文件的哈希、数据库结构和引用。同一 `pack_id` 同一修订号而内容不同的资料包会被拒绝。
从目录下载时还要核对归档长度和 SHA-256、清单哈希和解包后大小，它们必须与目录记录一致。

## 发布目录 {#layout}

```filetree {title="https://repo.pigsty.io/doc/（规划中）" wrap=true}
- catalog.json      # 签名目录：别名、版本与状态、每个归档的路径、大小和哈希
- pg/
  - 18.6-r4.crib
  - 17.11-r4.crib
  - 9.6.24-r4.crib
  - 20devel-r4.crib
- pgwiki/
  - r5.crib
```

完整目录有 31 个手册归档和 1 个 Wiki 归档。文件名是内容版本加修订号。别名只写在 `catalog.json` 里，不复制文件，也不建符号链接。

`catalog.json` 是一个签名信封，解码后的载荷包含目录身份、修订号、签发和过期时间、默认别名、别名表和版本表：

```json
{"format":"crib-signed-catalog-1","key_id":"…","payload":"<base64>","signature":"<base64>"}
```

```json
{
  "format_version": 3,
  "catalog_id": "crib-docs",
  "revision": 2026102901,
  "default_alias": "pg",
  "aliases": {"pg": "pg18", "pg18": "pg@18.6"},
  "releases": {
    "pg@18.6": [{
      "pack_id": "postgresql/manual/18",
      "content_version": "18.6",
      "lifecycle": "default",
      "pack_revision": 4,
      "languages": ["en", "zh-Hans"],
      "archive_path": "pg/18.6-r4.crib",
      "manifest_sha256": "…",
      "archive_sha256": "…"
    }]
  }
}
```

这是节选，正式载荷还有 `generated_at`、`expires_at`、大小、结构版本等字段。版本状态 `lifecycle` 的含义见[版本与别名](/docs/library/versions/#lifecycle)。

客户端只使用目录给出的归档路径：一级目录加一个文件名，不接受绝对地址、查询串、片段、百分号转义、反斜杠和 `..`，下载走与目录同源的 HTTPS。

## 不可变规则 {#immutable}

- 同一个 URL 的字节永远不变。内容有任何更正，都发新的内容版本或更高的修订号。
- 修订号按 `pack_id` 单调递增，换内容版本也不重置：18.6-r4 之后发布 18.7，修订号至少是 r5。
- 先上传并核对所有新归档，最后再替换 `catalog.json`。
- 目录修订号只增不减。`crib-pack distribute` 的 `--previous-catalog` 要求新修订号更大；`--validity` 设定目录有效期，默认 720 小时，范围 24 小时到 400 天。

## HTTP 缓存 {#http}

这些是发布端的要求，还没有在真实服务器上配置和测试。

| 对象 | 响应头 |
| --- | --- |
| `catalog.json` | `Cache-Control: no-cache` |
| 带版本和修订号的归档 | `Cache-Control: public, max-age=31536000, immutable`，`Content-Length`，强 `ETag`，支持 `Range`，不做内容转换 |

客户端续传时发送 `Range` 和 `If-Range`，规则见[断点续传](/docs/library/updates/#resume)。

---

反链：

- [11 月第一周进展](/blog/devlog/2026-11-first-week/)
- [文档](/docs/)
- [深链接](/docs/integrations/deep-links/)
- [工作原理](/docs/intro/architecture/)
- [管理资料库](/docs/library/manage/)
