Skip to content

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。客户端通过这个单一文件获取所有模板的 元数据和下载地址。

json
{
  "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"]
    }
  ]
}

完整字段说明:

字段类型说明
versionintSchema 版本,当前固定为 2
updatedAtstringISO 8601 UTC 时间戳
templates[].namestring模板唯一标识,[a-z0-9-]+
templates[].displayNamestring人类可读名称
templates[].truststring信任等级(见下文)
templates[].platformsobject平台 → 下载 URL + SHA256
templates[].requiredFontsarray依赖字体列表
templates[].previewImagesarrayCDN 上的预览图相对路径

manifest.json Schema

每个模板内嵌的元数据文件,通过 模板协议--manifest 标志导出。详细字段定义见 二进制协议文档

信任等级

信任等级决定了客户端 对模板的处理策略:

等级判定规则含义
officialrepo.owner === "Presto-io"官方维护,CI 直接提取
verified收录在 verified-templates.json 且已编译第三方源码,官方沙箱编译
community其他所有模板社区发布,不执行二进制
unrecorded不在注册中心客户端本地安装,未注册

当前现状:2 个 official 模板(gongwenjiaoan-shicao), verified 列表为空(verified-templates.json[]), 尚无 community 模板。

平台矩阵

每个模板最多支持 6 个平台组合:

OSarm64amd64
darwin (macOS)darwin-arm64darwin-amd64
linuxlinux-arm64linux-amd64
windowswindows-arm64windows-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 APIdiscovered.json
extract下载二进制、提取元数据discovered.jsonoutput/{name}/
buildDocker 沙箱编译 verified 模板verified-templates.jsonGitHub Release
compileTypst CLI 编译 SVG 预览output/{name}/output.typoutput/deploy/
index汇总生成 registry.json v2output/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 仓库结构

text
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.jsontemplates/registry.json
https://presto.c-1o.top/templates/gongwen/preview-1.svgtemplates/gongwen/preview-1.svg
https://presto.c-1o.top/templates/gongwen/manifest.jsontemplates/gongwen/manifest.json

缓存策略

通过 _headers 文件配置 Cloudflare Pages 的缓存行为:

资源类型浏览器缓存CDN 缓存设计意图
*.json60 秒5 分钟索引数据需要较快更新
*.svg1 小时24 小时预览图变化频率低
*.md5 分钟1 小时README 等文本内容
其他5 分钟1 小时默认策略

所有响应都设置了 Access-Control-Allow-Origin: *(CORS 全开放) 和 X-Content-Type-Options: nosniff


端到端流程

从模板开发者发布到用户使用的完整链路:

客户端侧:registry.go 缓存机制

Presto 客户端(internal/template/registry.go)实现了三级回退的 注册中心访问策略:

  1. 内存缓存:sync.RWMutex 保护的 registryCache 结构体
  2. 磁盘缓存:registry-cache.json 文件,TTL 为 1 小时
  3. 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)
RunnerGitHub-hosted UbuntuSelf-hosted macOS ARM64
权限contents: readcontents: write
PATPRESTO_PAT
执行二进制
网络隔离unshare --net无需(不执行二进制)
数据传递upload-artifactdownload-artifact

Docker 沙箱编译(Verified 模板)

Verified 模板的编译流程分为两个阶段,网络权限在阶段间切换:

  1. 依赖下载阶段:go mod download,允许网络
  2. 编译阶段:go build--network none 禁止网络

这确保编译阶段无法将源码或密钥外传。

社区模板的安全边界

社区模板的二进制由第三方编译发布,注册中心不执行这些二进制。 CI 仅通过 GitHub API 读取 manifest.jsonREADME.md, 不下载、不运行任何可执行文件。用户安装社区模板时,客户端会 显示信任等级警告。

Presto — Markdown to PDF