市场与签名
索引是一份小 JSON,置于静态托管之上;安装包走同源直链。两者的关联靠 SHA-256,信任靠签名。
为什么分成两份
索引与安装包分离:索引只有几十 KB,可以随时拉、可以缓存在本地; 安装包可能几十 MB,不需要每次都下载。用户每次启动只付索引的钱。
拉取失败时宿主保留旧缓存 —— 断网不该让用户失去已经能看到的内容。
自建源时,索引由你的发布流程生成:包内 manifest 决定 id 与版本, 文件本身决定 sha256,发布地址决定 url。这三样都不需要人再抄一遍 —— 抄错的后果不是「某个插件装不上」,而是整份索引拉取失败, 你的用户看到的只会是「市场打不开」。
索引长什么样
{
"updatedAt": "2026-10-14T03:44:08.159Z",
"signature": "X8eAOfDuNL5RcV5pZavB7QFoiA6ZPg/DwS/zHOF7y57jAcQCH2RQFcu14czfeapZh5oly0TXe5lsbglxGJpFAQ==",
"plugins": [
{
"id": "dev.tabtab.plugin.calculator",
"name": "计算器",
"version": "1.0.0",
"minHostVersion": "0.1.0",
"sha256": "4887af8fc2d07fddab0ad724668a0b575864803b25b8c63ac2a0e0363dac09a4",
"url": "https://<你的域名>/market/<插件名>-<版本号>.zip",
"description": "能算整条算式的计算器:四则、幂、阶乘、三角与对数函数,错误精确标到出错的字符。每次算完自动记进历史,历史可搜索、可复用、可删除。",
"author": "TABTAB",
"tags": [
"计算器",
"工具",
"剪贴板"
],
"permissions": [
"storage",
"clipboard:read",
"clipboard:write"
],
"official": true,
"signature": "",
"icon": "https://tabtab.dev/market/icons/calculator.svg",
"updatedAt": "2026-10-14",
"downloads": null,
"homepage": ""
}
]
}宿主的校验很严格,而且是整份校验:任何一条目的包地址不是 https、 sha256 不是 64 位十六进制、版本号非法,都会让整次拉取失败。
pnpm verify:market # 按宿主同一套规则校验产物索引发布一个包
- 构建插件,产物里带上
manifest.json与icon.svg。 - 压缩成
<name>-<version>.zip,根目录就是 manifest 所在层级。 - 发布 —— sha256、条目、下载地址、图标地址都会自动生成。
- 签名 —— 公钥已配置的宿主不签名就不加载这份索引。
- 发布前跑一次校验,确认这份索引宿主会接受。
pnpm sync:market # 从 market/ 下的 zip 重生成索引与站点目录发布闸门
声明 runtime.kind = "process" 的包会在发布那一刻被直接拒掉, 构建失败退出。宿主在安装时也会拦,但那是消费点的兜底 —— 索引一旦发布出去就已经在网上,宿主拦下安装也撤不回那个条目。
签名
Ed25519。私钥只放在 CI secret TABTAB_MARKET_SIGNING_SEED, 公钥编译进宿主(INDEX_SIGNING_PUBKEY,用户可审计)。 签名覆盖索引里除 signature 字段以外的全部内容 —— 包括每一条的下载地址与 sha256,所以中途改一改包地址就会当场对不上。
签名必须排在 sync:market 之后:索引的生成者每次都会把signature 重置成空串(内容一改,旧签名本就作废),顺序反了等于没签。pnpm build 已把三步排好;本地没设私钥时会跳过并告警。
TABTAB_MARKET_SIGNING_SEED=<私钥> TABTAB_REQUIRE_SIGNING=1 pnpm sign:market- 私钥不走命令行。命令行参数在同一台机器上对所有进程可见 (
ps、各进程的cmdline),CI 的构建日志也常把它们原样记下 —— 所以发布一律从环境变量读。 - 开发构建宽容。没有配置公钥时索引仍可拉取,但界面会明确告知 「该索引未经签名验证」,而不是静默当作可信内容展示。
- release 构建强制。
INDEX_SIGNING_PUBKEY为空时 商城功能直接停用 —— 不是警告。横幅在真实使用里没人看, 「配没配公钥」若不影响任何行为,一个忘记配公钥的发布就过去了。 - 验签钉在消费点。缓存对象与「它是可信的」之间没有绑定: 验签写在安装的那一刻,而不是「拉取时验过了就信」。
自建市场源
索引地址可在设置里覆盖(便于自建源)。有两条硬约束:
- 索引地址必须显式走 https。本机自建源(
http://localhost)放行。 - 条目里的
url与icon同样强制 https —— http 图标既是混合内容,也让索引作者能通过加载行为追踪谁在看哪个插件。
本地导入
带子进程的包、以及任何没进公开索引的包,走本地导入。 差别只有一处,但它必须被说清楚:本地包不经签名验证。 所以宿主的返回值里 signed 恒为 false,前端必须明示, 且装之前会先探查一遍权限、再明确告知「这个包会起子进程」。
当前官方市场
已上架 14 个包,索引更新于2026/10/14。 任何人都可以把这份目录复制走自己托管 —— 客户端不校验索引来源的主机名, 只认签名。