@codefusion-cc/sealed

中文文档
0.1.3发布于 2026/10/09MITTypeScriptES Module近 30 天下载 2131

秘密保存在数据库中,由AES-GCM从工人密钥下封入(每个密钥绑入保存地点,可以替换而不会丢失旧密钥),小秘密被封入收件人的X25519公用密钥 s

中文使用文档
文档目录5 节
  1. 钥匙
  2. 在工人中
  3. 一个新的密钥
  4. 从早先的代码移动
  5. 封入公用钥匙

@ 编码- cc/ 密封

保密应用程序在自己的数据库中保存(它所调用服务的 API 密钥, 表示人们粘贴到一个 admin 面板)被AES-256-GCM封入数据库从未持有的密钥:一个工人秘密. 副本或导出 数据库中没有任何人被泄露。 WebCrypto 只运行在工人、节点和德诺;它唯一的依赖 实值 @codefusion-cc/workers-crypto,用于 base64url 和摘要.

npm install @codefusion-cc/sealed

@codefusion-cc/sealed/box 把一个小秘密封在某人的公用钥匙上, 这样他们才打开它 谁不能传达它下级) (中文(简体) ).

钥匙

codefusion-console cloudflare workers:edit -- sh -c 'npx -p @codefusion-cc/sealed codefusion-sealed-key | npx wrangler secret put CONNECTIONS_KEY'

钥匙直接进入秘密 永远不要进入屏幕 Wrangler为这辆装满了硬币 命令,哪个 codefusion-console 删除退出时 : 没有 wrangler login 在麦克.

在工人中

import { loadKeyring } from '@codefusion-cc/sealed'

const keyring = await loadKeyring(env.CONNECTIONS_KEY) // null: no key, or the secret is not one
if (!keyring) throw new Error('Set CONNECTIONS_KEY first')

// Bound to where it is kept: the same text opens only for the same context.
const sealed = await keyring.seal(JSON.stringify({ token }), `connection:${id}`)

const opened = await keyring.open(row.sealed, `connection:${row.id}`)
if (opened.ok) {
  use(JSON.parse(opened.value))
  if (opened.stale) await save(row.id, await keyring.seal(opened.value, `connection:${row.id}`))
} else {
  // 'unknown-key': the key that sealed it is gone; 'tampered': changed, or moved from another row;
  // 'malformed': not a sealed value.
  askForItAgain(opened.reason)
}
  • 一个密封值是 v1.<key id>.<iv>.<ciphertext> (基数64url). 密钥ID是密钥的前8个十六进制数字 SHA-256:它命名密钥而不将其送出,所以一个应用程序可以显示其行需要的密钥.
  • open 绝不要为一行人而抛下任何物; seal 财务报告和审定财务报表 open 当给定一个不是字符串的上下文时, 扔出, 而当平台的 WebCrypto 失败时拒绝(如 loadKeyring 是,哪个 然后在下次召唤时再次尝试这个秘密. tampered 财务报告和审定财务报表 unknown-key 总是指排队, 从来没有停赛.
  • open 给出准确的被封存的文本,包含一个主字节顺序标记(U+FEFF)或白空格. 清理封口前有人贴的东西: value.trim() 两者都放下,所以从保存的文件复制一个密钥 BOM仍然符合请求标题.
  • 密钥在每次隔离时被导入一次,用于同一密文.

一个新的密钥

密钥可能先有数个新密钥,除以逗号或白相间. 最新的封印 每把钥匙打开, 和什么更古老的密钥密封打开作为 stale: true所以应用程序在读它的时候又封了它.

codefusion-console cloudflare workers:edit -- npx wrangler secret put CONNECTIONS_KEY   # paste: <new key>,<old key>

一旦每行再次被封(每行) open 说 stale: false),只将新密钥放入密钥. 没有 旧密钥没有退路:它密封的东西必须再输入.

从早先的代码移动

前面写入的值 <iv>.<ciphertext> (标准基数64,无上下文,如柴油机插件所做的) open(value, context, { legacy: true }),总是如所僵化:按所读再封相. 离开 legacy 关一次 由于该格式的值无一行约束,因此没有留下.

封入公用钥匙

@codefusion-cc/sealed/box 是一个通过一个不能读取的服务器的秘密: 页面封存到 使用它的人的公钥(在某人的Mac上的中继器,设备),服务器存储或转发被封件 文本,只有私钥的持有人才能打开。 它的工作像 Libsodium 的密封箱, 在WebCrypto,所以 同样的代码运行在浏览器(Chrimium,Firefox,Safari),节点20和后期,以及Workers.

在收件人上, 在每个开始时: 只有在还没有密钥文件时才会新建密钥 。 旧文件中的新密钥会留下 每个封入到旧公钥的盒子都无法打开,所以其他的(一个它无法读取的文件,一个没有钥匙的文件,a) WebCrypto 失败 停止开始 :

import { link, readFile, rm, writeFile } from 'node:fs/promises'
import { generateBoxKeys, loadBoxKeys, saveBoxKeys, type BoxKeys } from '@codefusion-cc/sealed/box'

async function recipientKeys(path: string): Promise<BoxKeys> {
  let text: string
  try {
    text = await readFile(path, 'utf8')
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error // unreadable is not missing
    const keys = await generateBoxKeys()
    // It holds the private key. Written whole beside it, then linked in only while the path is still free: a start
    // that dies midway leaves no half-written key file, and two starts at once never write over each other's keys.
    const draft = `${path}.${crypto.randomUUID()}.new`
    await writeFile(draft, await saveBoxKeys(keys), { mode: 0o600, flag: 'wx' })
    try {
      await link(draft, path)
    } catch (error) {
      if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error
      return recipientKeys(path) // another start made them first: use those
    } finally {
      await rm(draft, { force: true })
    }
    return keys
  }
  const keys = await loadBoxKeys(text.trim()) // rejects when WebCrypto fails, rather than saying there are none
  if (!keys) throw new Error(`${path} holds no box keys: move it aside to make new ones`)
  return keys
}

const keys = await recipientKeys(path)
register(keys.publicKey) // 43 characters of base64url: give it to the server, which hands it to the page

在页面中,封入服务器提供给它的公钥:

import { sealBox } from '@codefusion-cc/sealed/box'

const sealed = await sealBox(relay.publicKey, prompt, `terminal:${relay.id}:${promptId}`) // null: not a box public key

在接收者上打开服务器转发的内容:

import { openBox } from '@codefusion-cc/sealed/box'

const opened = await openBox(keys, sealed, `terminal:${relayId}:${promptId}`)
if (opened.ok) run(opened.text) // or opened.bytes
// 'malformed': not a sealed box, or longer than maxBytes (1 MiB unless given);
// 'tampered': changed, sealed to another key, or for another context.
  • 它会做什么:每个印章会制作一对新鲜的X25519密钥;它与收件人密钥一致的秘密去 通过HKDF-SHA256(用两种公用钥匙进行盐分,其信息中的内容)到一次使用的AES-256-GCM密钥. 这个 密封文本为 box1.<ephemeral public key>.<iv>.<ciphertext>,每个部分基数64url不粘贴.
  • A. 背景情况: 打开框的字符串名称, 如 seal上面写着 一个框移动到另一个 上下文(另一个提示,另一个中继)不会打开。 把所有的ID 接收检查.
  • 谁的钥匙:无论谁交出页面,收件人的公钥决定谁打开盒子. 一个掌上电脑的服务器 打开它自己的密钥 而不是读取页面封条的内容 所以盒子对存储和保存的服务器保密 转发它(它的数据库,它的日志,其中任何一个都漏出),而不是隐藏在钥匙上的。 这很重要, 页面获取或检查密钥的另一种方式(在收件人上显示指纹).
  • 匿名发送者:一个盒子没有说是谁封的. 谁有公用钥匙,谁可以封起来,那么一个盒子 hold是来自外部的输入:认证谁单独要求它(服务器在转发之前是这样做的).
  • 没有自己的保护: 同一框为同一上下文再次打开. 使背景独特(a) )并让收件人拒绝它已经运行的id.
  • 钥匙在休息数字 : saveBoxKeys 写私钥的JWK(RFC 8037); 保存在只有所有者阅读的地方. loadBoxKeys 返回时为无效,包括两个半个不是一对的JWK,并加载 私人密钥不可提取,除非 { extractable: true } (只需要再保存一次).
  • 错误数字 : sealBox 解析一个不是盒子公钥的收件人为无效(或其中少数 X25519 键之一) - 小型命令,其中不包含任何秘密; openBox 永远不要为封存的文本丢弃。 都投给 参数 编程错误给定的(不是字符串的上下文,不是框键的密钥),以及每个 函数,它使用 WebCrypto 拒绝在失败时使用平台本身的错误(旧浏览器中没有 X25519,一个已损坏) 约束:无效, malformed 财务报告和审定财务报表 tampered 只描述过输入.
  • 小秘密( 暗号): a box 是 AES-GCM 信件, 保存在全内存中. openBox 拒绝次数超过 maxBytes 联 合 国 解码任何内容之前的纯文本( 默认 1 MiB) .

@codefusion-cc/realtime账户密钥( e)packages/realtime/src/accountKeys.ts在P-256上有一个自己的盒子 使用自己的格式,设备已经将数据封存起来,所以它们留在上面而不是移动到这里.

版本与发布记录

@codefusion-cc/sealed 共发布 7 个版本,首次发布于 2026/10/01;过去 12 个月发布了 7 个版本。

版本发布日期距今
0.1.3latest2026/10/091 个月前
0.1.22026/10/031 个月前
0.1.02026/10/021 个月前
1.1.12026/10/021 个月前
1.1.02026/10/021 个月前
1.0.12026/10/021 个月前

关于 @codefusion-cc/sealed 的常见问题

@codefusion-cc/sealed 是什么?

秘密保存在数据库中,由AES-GCM从工人密钥下封入(每个密钥绑入保存地点,可以替换而不会丢失旧密钥),小秘密被封入收件人的X25519公用密钥 s

如何安装 @codefusion-cc/sealed?

在项目目录运行 npm install @codefusion-cc/sealed,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 codefusion-sealed-key,可以用 npx codefusion-sealed-key 直接运行。

@codefusion-cc/sealed 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@codefusion-cc/sealed 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@codefusion-cc/sealed 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@codefusion-cc/sealed 可以商用吗?

@codefusion-cc/sealed 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@codefusion-cc/sealed 的下载量有多少?

根据 npm 官方下载统计,@codefusion-cc/sealed 在 2026/10/10 – 2026/11/08 这 30 天内被下载 2,131 次,日均约 71 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@codefusion-cc/sealed 还在维护吗?

最新版本 0.1.3 发布于 2026/10/09(1 个月前);至今共发布 7 个版本,其中过去 12 个月发布了 7 个;首次发布于 2026/10/01。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。