Cachet

开放接口

任何网站和应用都可以免费调用 Cachet 的接口,生成和打开限时加密消息。通过接口生成的密文与 Cachet 输入法、官网完全互通。

接口地址http://1.12.58.194:82
格式JSON,UTF-8。所有接口都允许跨域调用(CORS),无需注册,无需密钥。
限流每个 IP 每分钟:创建 60 次、读取 600 次。超出返回 429。

两种接入方式

零知识接口(推荐)服务端加解密接口
接口POST /v1/keys
GET /v1/keys/{id}
POST /v1/seal
POST /v1/open
加解密在哪里做你的设备 / 浏览器(用我们的 SDK)我们的服务器
服务器能否看到原文不能能(只在内存中处理,不保存、不记录)
适合网页、App、对隐私有要求的场景后端脚本、机器人、快速对接

方式一:网页 SDK(零知识)

本站运行的就是这份 SDK:sdk/cachet.js(约 85 KB,未压缩,可直接审阅)。下载后放到你自己的网站,以 ES 模块引入:

import * as Cachet from './cachet.js';

// 加密:有效期 1 小时,可选访问密码、阅后即焚
const { ciphertext, expiresAt } = await Cachet.seal('今晚八点老地方见', {
  ttl: 3600,            // 秒,30 秒 ~ 30 天
  password: '',         // 可选
  burn: false,          // true = 只能打开一次
  style: 'han',         // 'han' | 'latin' | 'invisible'
  server: 'http://1.12.58.194:82',
});

// 解密:可以传入整段聊天文字,SDK 会自己找出密文
try {
  const { text, expiresAt, reads } = await Cachet.open(ciphertext, { password: '' });
} catch (e) {
  // e.code: not_cachet | password_required | wrong_password | expired | consumed | not_found
}

// 只检查、不打开(不消耗打开次数)
const info = Cachet.inspect(ciphertext);   // { hasPassword, burn, ... } 或 null

方式一:零知识接口

如果你在其他语言里自己实现加解密,只需要这两个接口;加密算法见 协议。

POST /v1/keys:创建钥匙

curl -X POST BASE/v1/keys -H 'Content-Type: application/json' \
  -d '{"ttl": 3600, "maxReads": 0}'
参数说明
ttl有效期,秒。30 ~ 2592000(30 天)
maxReads最多可读取次数,0 表示不限。阅后即焚填 1,上限 100
201 {"id": "Rk2x…", "key": "Q4m…", "expiresAt": 1791900000, "now": 1791896400}

id 和 key 都是不带填充的 base64url,id 解码后 12 字节,key 32 字节。

GET /v1/keys/{id}:读取钥匙

curl BASE/v1/keys/Rk2x…
200 {"key": "Q4m…", "expiresAt": 1791900000, "now": 1791896500, "reads": 1, "maxReads": 0}
410 {"error": "expired",  "expiresAt": …}       已过期,钥匙已抹除
410 {"error": "consumed", "reads": 1, …}        阅后即焚,已被读取
404 {"error": "not_found"}

每次成功读取都会计数;达到 maxReads 的那一次读取之后,钥匙立即抹除。

方式二:服务端加解密接口

这两个接口由服务器完成加解密,服务器会在内存中短暂处理原文。我们不保存、不记录,但如果你的场景不允许任何第三方经手原文,请使用方式一。

POST /v1/seal:加密

curl -X POST BASE/v1/seal -H 'Content-Type: application/json' \
  -d '{"text": "今晚八点老地方见", "ttl": 3600, "password": "", "burn": false, "style": "han"}'
201 {"ciphertext": "「…」", "id": "Rk2x…", "expiresAt": 1791900000, "now": 1791896400}

text 最多 10000 字;style 可选 han(默认,最短)、latin、invisible。

POST /v1/open:解密

curl -X POST BASE/v1/open -H 'Content-Type: application/json' \
  -d '{"ciphertext": "朋友发来的:「…」", "password": ""}'
200 {"text": "今晚八点老地方见", "expiresAt": 1791900000, "now": …, "reads": 1}
401 {"error": "password_required"}
403 {"error": "wrong_password"}      密码错误不计入打开次数
410 {"error": "expired" | "consumed"}
422 {"error": "not_cachet" | "private_message"}

协议

如果你想用其他语言实现零知识加解密,这是完整的格式。安卓、服务器、网页三端用同一组测试向量逐字节核对过。

密文字节 = 0x18 ‖ flags(1) ‖ keyId(12) ‖ AES-256-GCM(K, nonce = 12 个 0x00, aad = 前 14 字节, 明文)
flags:     bit0 = 有访问密码,bit1 = 阅后即焚
K =        HKDF-SHA256(ikm = key, salt = keyId, info = "cachet v2 public", 32)
有密码时:  ikm = key ‖ PBKDF2-SHA256(UTF-8(密码), salt = keyId, 100000 次, 32)
明文 =     1 字节格式 ‖ 内容;0x00 UTF-8,0x01 UTF-16BE,0x02 raw DEFLATE(UTF-8)

密文字节再转写成文字: