Skip to content

二进制协议速查

简单理解

Presto 的模板不是一堆文件,而是一个可执行程序。你把 Markdown 文本喂给它,它吐出排版代码(Typst)。就这么简单。

想象一个黑盒子:左边塞进去 Markdown,右边出来 Typst。这个黑盒子还能回答几个问题——"你是谁?"(--manifest)、"给个示例?"(--example)、"什么版本?"(--version)。Presto 主应用通过这四种方式和模板交互,没有其他途径。

模板二进制运行在严格的沙箱中:30 秒超时、无网络、无文件写入、最小环境变量。这意味着模板代码不可能偷数据或搞破坏。


协议速查表

四种调用方式

调用方式命令stdinstdout用途
转换./binaryMarkdown 文本Typst 代码核心功能:把 Markdown 变成排版代码
元数据./binary --manifestmanifest JSON安装时提取模板信息
示例./binary --exampleMarkdown 文本展示模板效果
版本./binary --version版本号字符串版本检查与更新判断

安全限制

限制项说明
执行超时30 秒单次调用最长时间
构建超时300 秒(5 分钟)模板构建最长时间
manifest 大小上限1 MB--manifest 输出上限
示例大小上限1 MB--example 输出上限
Typst 输出上限10 MB转换输出上限
构建产物上限50 MB编译后二进制上限
环境变量PATH=/usr/local/bin:/usr/bin:/bin最小环境,无其他变量
网络访问禁止macOS sandbox-exec / Linux unshare --net
文件写入禁止只能通过 stdout 输出
CLI flag仅上述四种不得添加协议外的 flag

三层安全防护

层级机制作用
静态分析禁止 import 黑名单编译前拦截危险依赖
运行时沙箱macOS sandbox-exec / Linux unshare --net隔离网络和文件系统
输出验证格式检查确保 stdout 只包含合法内容

交互时序图


详细解释

1. 转换:核心功能

这是模板存在的意义。Presto 把用户写的 Markdown 通过 stdin 传给模板二进制,二进制处理后通过 stdout 返回 Typst 源码。

内部处理流程:

  1. splitFrontmatter() — 分离 YAML 头部(页面配置)和正文
  2. writePageSetup() — 根据 frontmatter 字段生成 Typst 页面设置代码
  3. renderBody() — 用 goldmark 解析 Markdown AST,逐节点转换为 Typst 语法

关键约束:

  • stdin 只接受 Markdown 文本
  • stdout 只能输出合法的 Typst 源码
  • 不能读写任何文件,不能访问网络
  • 超过 30 秒自动终止

2. 元数据:--manifest

安装模板时,Presto 第一件事就是调用 --manifest 拿到模板的自我描述。这个 JSON 告诉 Presto:

  • 模板叫什么、谁写的、什么版本
  • 需要哪些字体
  • frontmatter 支持哪些字段(类型、默认值、格式)

manifest 数据在编译时通过 Go 的 //go:embed 嵌入二进制,不是运行时生成的。

3. 示例:--example

返回一段示例 Markdown,用于:

  • 模板商店的预览展示
  • 用户首次使用时的起步模板
  • 自动化测试的输入数据

同样通过 //go:embed 嵌入。

4. 版本:--version

返回版本号字符串,从嵌入的 manifest.json 中读取 version 字段。用于:

  • 检查模板是否有更新
  • 兼容性判断(配合 minPrestoVersion

5. 嵌入机制

模板是"二进制嵌入模型",不是"文件复制模型"。所有资源在编译时打包进可执行文件:

go
//go:embed manifest.json
var manifestData []byte

//go:embed example.md
var exampleData []byte

这意味着:

  • 分发时只有一个文件,不需要额外的资源目录
  • 运行时不依赖外部文件,不会出现"找不到配置"的问题
  • 沙箱限制更容易实施——二进制不需要读取任何外部文件

完整 manifest.json 示例

json
{
  "name": "academic-paper",
  "displayName": "学术论文",
  "description": "适用于中文学术论文的排版模板,支持摘要、关键词、参考文献等常见结构",
  "version": "1.2.0",
  "author": "Zhang San <zhangsan@example.com>",
  "license": "MIT",
  "minPrestoVersion": "0.5.0",
  "keywords": ["学术", "论文", "中文", "LaTeX 替代"],
  "requiredFonts": [
    {
      "name": "NotoSerifCJKsc",
      "displayName": "思源宋体",
      "url": "https://github.com/notofonts/noto-cjk/releases"
    },
    {
      "name": "NotoSansCJKsc",
      "displayName": "思源黑体",
      "url": "https://github.com/notofonts/noto-cjk/releases"
    }
  ],
  "frontmatterSchema": {
    "title": {
      "type": "string",
      "default": "未命名论文"
    },
    "author": {
      "type": "string",
      "default": "佚名"
    },
    "date": {
      "type": "string",
      "format": "date"
    },
    "abstract": {
      "type": "string",
      "default": ""
    },
    "keywords": {
      "type": "string",
      "default": ""
    },
    "twocolumn": {
      "type": "boolean",
      "default": false
    },
    "fontsize": {
      "type": "string",
      "default": "12pt"
    },
    "papersize": {
      "type": "string",
      "default": "a4"
    },
    "margin": {
      "type": "string",
      "default": "2.5cm"
    },
    "linestretch": {
      "type": "number",
      "default": 1.5
    }
  }
}

字段说明:

字段必填说明
name模板唯一标识符,小写字母 + 连字符
displayName用户可见的模板名称
description模板用途描述
version语义化版本号(semver)
author作者信息
license开源许可证
minPrestoVersion最低兼容的 Presto 版本
keywords搜索关键词
requiredFonts依赖的字体列表
frontmatterSchema用户可配置的 frontmatter 字段定义

frontmatterSchema 支持的 type 值:

type说明示例默认值
"string"文本"未命名论文"
"number"数字1.5
"boolean"布尔值false

可选的 format 字段用于提示 UI 渲染方式(如 "date" 会显示日期选择器)。

Presto — Markdown to PDF