Presto 注册中心架构
模板注册中心是 Presto 模板生态的分发平台,采用静态 JSON + CDN 架构——没有后端服务器, 没有数据库,所有数据在 CI 中生成为静态文件,通过 Cloudflare Pages 分发到全球边缘节点。
系统全景
整个系统由两个仓库协作完成:
| 仓库 | 职责 | 运行环境 |
|---|---|---|
template-registry | 数据收集、处理、构建 | GitHub Actions |
registry-deploy | 静态托管、CDN 分发 | Cloudflare Pages |
template-registry 负责"脏活"——搜索模板、下载二进制、提取元数据、 编译预览图、生成索引。registry-deploy 只做一件事:把静态文件原样 托管到 CDN,根目录即部署内容,无需构建步骤。
数据格式
registry.json v2 Schema
注册中心的核心产物是 registry.json,版本号为 2。客户端通过这个单一文件获取所有模板的 元数据和下载地址。
{
"version": 2,
"updatedAt": "2026-02-25T06:56:46Z",
"templates": [
{
"name": "gongwen",
"displayName": "类公文模板",
"description": "符合 GB/T 9704-2012 标准的类公文排版",
"version": "1.0.0",
"author": "Presto-io",
"repo": "Presto-io/presto-official-templates",
"license": "MIT",
"category": "公文",
"keywords": ["公文", "通知", "报告"],
"trust": "official",
"platforms": {
"darwin-arm64": {
"url": "https://github.com/.../presto-template-gongwen-darwin-arm64",
"sha256": "205aaa..."
}
},
"minPrestoVersion": "0.1.0",
"requiredFonts": [{ "name": "FZXiaoBiaoSong-B05", "displayName": "方正小标宋" }],
"previewImages": ["gongwen/preview-1.svg"]
}
]
}完整字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
version | int | Schema 版本,当前固定为 2 |
updatedAt | string | ISO 8601 UTC 时间戳 |
templates[].name | string | 模板唯一标识,[a-z0-9-]+ |
templates[].displayName | string | 人类可读名称 |
templates[].trust | string | 信任等级(见下文) |
templates[].platforms | object | 平台 → 下载 URL + SHA256 |
templates[].requiredFonts | array | 依赖字体列表 |
templates[].previewImages | array | CDN 上的预览图相对路径 |
manifest.json Schema
每个模板内嵌的元数据文件,通过 模板协议的 --manifest 标志导出。详细字段定义见 二进制协议文档。
信任等级
信任等级决定了客户端 对模板的处理策略:
| 等级 | 判定规则 | 含义 |
|---|---|---|
official | repo.owner === "Presto-io" | 官方维护,CI 直接提取 |
verified | 收录在 verified-templates.json 且已编译 | 第三方源码,官方沙箱编译 |
community | 其他所有模板 | 社区发布,不执行二进制 |
unrecorded | 不在注册中心 | 客户端本地安装,未注册 |
当前现状:2 个 official 模板(gongwen、jiaoan-shicao), verified 列表为空(verified-templates.json 为 []), 尚无 community 模板。
平台矩阵
每个模板最多支持 6 个平台组合:
| OS | arm64 | amd64 |
|---|---|---|
| darwin (macOS) | darwin-arm64 | darwin-amd64 |
| linux | linux-arm64 | linux-amd64 |
| windows | windows-arm64 | windows-amd64 |
二进制命名规则:presto-template-{name}-{os}-{arch}[.exe]
CI 管道架构
主流程:update-registry.yml
每日 UTC 08:00 定时触发,也支持手动触发(可选 force_rebuild)。 核心设计是双 Job 安全隔离——extract 在低权限环境运行不可信代码, compile-and-deploy 在高权限环境处理受信数据。
Job 1(extract)的权限和环境:
- 运行在
ubuntu-latest(GitHub-hosted runner) - 权限:
contents: read——只读,无法推送代码 - 执行不可信的模板二进制(官方模板),通过
safe_run()沙箱保护 - 产物通过
upload-artifact传递给 Job 2
Job 2(compile-and-deploy)的权限和环境:
- 运行在
self-hosted, macOS, ARM64 - 权限:
contents: write——可推送代码 - 使用
PRESTO_PAT跨仓库推送到registry-deploy - 只处理 Job 1 产出的静态文件,不执行任何二进制
这种隔离确保:即使模板二进制是恶意的,它也无法获得写权限或 访问 PAT。
Verified 模板编译:build-verified.yml
当 verified-templates.json 变更并推送到 main 时触发。 同样采用双 Job 架构:
- Job 1(build):克隆第三方源码 → Docker 沙箱交叉编译 6 平台 → 上传到
template-registry的 GitHub Release - Job 2(compile-and-deploy):与主流程相同,编译 SVG 并部署
Docker 沙箱编译的安全约束:
CGO_ENABLED=0:纯静态链接,无 C 依赖--network none:编译阶段禁止联网--read-only+tmpfs /tmp:只读文件系统- 内存限制 2GB,CPU 限制 2 核
- 5 分钟超时,50MB 产物大小限制
版本检测自动化:check-versions.yml
每日 UTC 08:30 定时运行(与主流程错开 30 分钟),检查 verified-templates.json 中的模板是否有新版本发布:
这形成了一个半自动化链路:版本检测自动化,但编译需要人工审核 PR 后才触发,保留了人在回路中的安全控制。
构建脚本概览
scripts/build_registry.py 提供 5 个子命令,按流水线顺序执行:
| 子命令 | 功能 | 输入 | 输出 |
|---|---|---|---|
discover | 搜索 GitHub topic + 官方仓库 | GitHub API | discovered.json |
extract | 下载二进制、提取元数据 | discovered.json | output/{name}/ |
build | Docker 沙箱编译 verified 模板 | verified-templates.json | GitHub Release |
compile | Typst CLI 编译 SVG 预览 | output/{name}/output.typ | output/deploy/ |
index | 汇总生成 registry.json v2 | output/deploy/ | registry.json |
官方模板与社区模板的处理差异:
| 步骤 | 官方模板 | 社区模板 |
|---|---|---|
| 元数据获取 | 下载二进制,执行 --manifest | 从 GitHub API 读取 manifest.json |
| 示例文档 | 执行 --example 导出 | 不获取 |
| Typst 源码 | 管道转换 stdin → binary → stdout | 不生成 |
| SVG 预览 | Typst CLI 编译 | 无预览 |
| 二进制执行 | 是(沙箱内) | 否 |
安全措施摘要:
safe_run():清洗环境变量(仅保留PATH),30 秒超时- Linux 上使用
unshare --net隔离网络,阻止二进制外传数据 - 大小限制:manifest < 1MB,example < 1MB,typst < 10MB
- SHA256 校验:下载二进制后与 Release 中的
SHA256SUMS比对
CDN 部署
registry-deploy 仓库结构
registry-deploy/
_headers ← Cloudflare Pages 自定义 headers
_redirects ← 重定向规则
templates/
registry.json ← 模板索引
gongwen/
manifest.json
README.md
example.md
preview-1.svg, preview-2.svg
hero-frame-0.svg, hero-frame-1.svg, hero-frame-2.svg
jiaoan-shicao/
manifest.json, README.md, example.md, preview-*.svg
plugins/ ← 将来:插件注册中心
agent-skills/ ← 将来:Agent 技能注册中心URL 映射
仓库根目录直接映射为 CDN 根路径:
| CDN URL | 对应文件 |
|---|---|
https://presto.c-1o.top/templates/registry.json | templates/registry.json |
https://presto.c-1o.top/templates/gongwen/preview-1.svg | templates/gongwen/preview-1.svg |
https://presto.c-1o.top/templates/gongwen/manifest.json | templates/gongwen/manifest.json |
缓存策略
通过 _headers 文件配置 Cloudflare Pages 的缓存行为:
| 资源类型 | 浏览器缓存 | CDN 缓存 | 设计意图 |
|---|---|---|---|
*.json | 60 秒 | 5 分钟 | 索引数据需要较快更新 |
*.svg | 1 小时 | 24 小时 | 预览图变化频率低 |
*.md | 5 分钟 | 1 小时 | README 等文本内容 |
| 其他 | 5 分钟 | 1 小时 | 默认策略 |
所有响应都设置了 Access-Control-Allow-Origin: *(CORS 全开放) 和 X-Content-Type-Options: nosniff。
端到端流程
从模板开发者发布到用户使用的完整链路:
客户端侧:registry.go 缓存机制
Presto 客户端(internal/template/registry.go)实现了三级回退的 注册中心访问策略:
- 内存缓存:
sync.RWMutex保护的registryCache结构体 - 磁盘缓存:
registry-cache.json文件,TTL 为 1 小时 - CDN 拉取:
https://presto.c-1o.top/templates/registry.json
回退逻辑:内存命中 → 磁盘命中且未过期 → CDN 拉取 → CDN 失败时 返回过期的磁盘缓存 → 全部失败返回 nil。
安全约束:
- 响应体大小限制 10MB(
io.LimitReader,SEC-13) - 请求超时 15 秒
- 重定向校验:只允许跳转到
presto.c-1o.top和已知下载域名 (SEC-46) - 缓存文件权限
0600(SEC-45)
客户端提供的查询接口:
VerifySHA256(name, hash):校验本地二进制是否与注册中心一致LookupTrust(name):查询模板信任等级LookupByRepo(ownerRepo):按仓库名查找模板(SEC-39,服务端 用此获取可信下载 URL)
安全模型
信任链
双 Job 权限隔离
这是整个安全模型的基石。核心原则:执行不可信代码的环境绝不持有 写权限或敏感凭据。
| 维度 | Job 1 (extract/build) | Job 2 (compile-and-deploy) |
|---|---|---|
| Runner | GitHub-hosted Ubuntu | Self-hosted macOS ARM64 |
| 权限 | contents: read | contents: write |
| PAT | 无 | PRESTO_PAT |
| 执行二进制 | 是 | 否 |
| 网络隔离 | unshare --net | 无需(不执行二进制) |
| 数据传递 | upload-artifact | download-artifact |
Docker 沙箱编译(Verified 模板)
Verified 模板的编译流程分为两个阶段,网络权限在阶段间切换:
- 依赖下载阶段:
go mod download,允许网络 - 编译阶段:
go build,--network none禁止网络
这确保编译阶段无法将源码或密钥外传。
社区模板的安全边界
社区模板的二进制由第三方编译发布,注册中心不执行这些二进制。 CI 仅通过 GitHub API 读取 manifest.json 和 README.md, 不下载、不运行任何可执行文件。用户安装社区模板时,客户端会 显示信任等级警告。
