@cipherstash/protect-ffi

中文文档
0.33.0发布于 2026/10/02MITTypeScriptESM + CJS构建来源已验证近 30 天下载 5206

原始 FFI 绑定到 CipherStash 客户端 SDK — 权限@ cipherstash/stack

中文使用文档
文档目录11 节
  1. 大楼
  2. 本地设置
  3. 探索
  4. EQL 版本选择
  5. 大缩略语
  6. 错误
  7. 可用的脚本
  8. 项目布局
  9. 融合测试
  10. 释放
  11. 学习更多

CipherStash 客户端 FFI (@cipherstash/protect-ffi(中文(简体) )

[大 如果您想要在您的应用程序中执行此软件包, 请使用官方 @cipherstash/stack 软件包。 。 。 .

该项目为CepherStash客户端Rust SDK提供了JS装订,并被 创建- 尼昂。 。 。 .

大楼

大楼需要 已支持的节点和rust版本。 。 。 .

要运行建设,运行:

$ npm run build

此命令使用 @neon-rs/cli (英语) 用于从输出中组装二进制节点加通 cargo。 。 。 .

本地设置

认证 stash CLI来建立你的地方环境。 它通过 npx - 不需要单独安装:

npx stash auth login

您将被提示注册或创建账户 .

探索

建成后 protect-ffi,您可以在节点控制台探索其出口. 代表团必须出示全权证书,以便 newClient() 成功——从 CipherStash 剖面商店、环境变量或明确 authStrategy 转至 newClient()对于地方发展来说,最简单的选择是: npx stash auth login,它包含配置文件商店。 或者设置 CS_WORKSPACE_CRN, (中文(简体) ), CS_ACCESS_KEY,则 CS_CLIENT_ID编号 :CS_CLIENT_KEY 键盘在您的环境中 .

$ npm i
$ npm run build
$ node
> const addon = require(".");
> const client = await addon.newClient({ encryptConfig: {v: 1, tables: {users: {email: {indexes: {ore: {}, match: {}, unique: {}}}}}} });
> const ciphertext = await addon.encrypt(client, { plaintext: "plaintext", column: "email", table: "users" });
> const plaintext = await addon.decrypt(client, { ciphertext });
> console.log({ciphertext, plaintext});

EQL 版本选择

newClient 接受一个 eqlVersion 选项选择线条格式 encrypt 编号 : encryptBulk 编号 : encryptQuery 发出 :

// EQL v2 (default) — the `eql_v2_encrypted` payload format
const v2 = await addon.newClient({ encryptConfig })

// EQL v3 — payloads for the per-capability `eql_v3` domains
const v3 = await addon.newClient({ encryptConfig, eqlVersion: 3 })

与 eqlVersion: 3,每列有效载荷的目标 eql_v3 域名 来源于 cast_as 和索引:

每个公示栏域都包含一个 eql_v3_ 前缀( 故为 SQL) 列类型是 public.eql_v3_text_eq; 仅词查询双胞胎不 (单位:千美元)eql_v3.query_text_eq),因为 eql_v3 计划已经把它们改了.

cast_as家属索引域名
texttextunique + 键 ore + 键 matcheql_v3_text_search_ore
texttextunique + 键 ope + 键 matcheql_v3_text_search
texttextunique + 键 oreeql_v3_text_ord_ore
texttextmatcheql_v3_text_match
texttextuniqueeql_v3_text_eq
int 编号 : small_int 编号 : bigintinteger 编号 : smallint 编号 : bigintore (有无) unique(中文(简体) )eql_v3_<family>_ord_ore
int 编号 : small_int 编号 : bigintinteger 编号 : smallint 编号 : bigintuniqueeql_v3_<family>_eq
number 编号 : decimal 编号 : date 编号 : timestampdouble 编号 : numeric 编号 : date 编号 : timestamp见上文见上文
任意平面—— 说无仅存储域( e)eql_v3_text, (中文(简体) ), eql_v3_integer, (...) (中文(简体) )
booleanboolean不仅仅是eql_v3_boolean (只存储——任何索引错误)
jsonjsonste_vec (必须, compat 模式)eql_v3_json_search

注释:

  • 最富有的匹配域获胜,它必须覆盖每个配置 能力——一种沉默地放弃术语错误的组合 (例如:. unique + 键 match 或者说 ore + 键 match 文本:没有单个域 带有这些术语集,所以添加缺失的索引以达到搜索 域或将能力分出各列。 仅使用 Scalar 配置 可使用 eqlVersion: 2; 包含 ste_vec 需要v3时.
  • 两个文本搜索域仅在它们携带的顺序名词上有所不同: eql_v3_text_search_ore 携带 ob (德文), eql_v3_text_search 携带 op (OPE) (英语). 配置两者 ore 财务报告和审定财务报表 ope 选择 ORE .
  • 例外: 投出一个 任期 等会就没事了 能力 活下来了. 非文本排序域只携带 ob这样 unique + 键 ore 编号 数字列投放 hm ——平等仍然通过ORE操作者实现.
  • 排序文本需要 unique 指数(eql_v3_text_ord_ore 编号 : eql_v3_text_ord_ope 携带 hm + 键 ob编号 :op(三); ore- 只有文本错误.
  • A 类 json 栏 ste_vec 索引必须使用 compat 模式(模式) cipherstash-client 默认自0.40.0起). v3 命令 SteVec 条目 cLLW -OPE 游戏 op 任期;a standard- 模式索引发布 CLLW- ORE oc,哪个 没有机械转换,所以在配置时出现这样的列错误.
  • 包含 ste_vec 默认为 EQL v3. 明确 eqlVersion: 2 被拒绝,因为客户端不能代表新的 以 v2 有线格式选择绑定 SteVec 信封.
  • decrypt 接受 两者 格式,不论 eqlVersion,所以v2和 v3 数据可以在迁移期间共存.
  • v3 查询加密返回索引名词只操作符: scalar 查询 生成列域的查询双双 ({v, i, <terms>},没有 c 密码)——与 col = $1::jsonb::eql_v3.query_<name> (或 命令操作员,或 @@ (原始内容存档于2017-09-21). for match. 剧本总是带着所有的东西 列域术语,以 indexType 被问及。 贾森 封装查询生成 eql_v3.query_json 针和 ste_vec_selector 查询返回赤色选择器散列 页:1 -> 编号 : ->> 操作员。 JSON 路径中的精确平等 ste_vec_value_selector 与 {path, value} 返回一个输入 适合 GIN 支持的仅选取封闭针 @> 操作员.
  • ope- 索引列映射到 <family>_ord_ope 并携带 op (CLLW-OPE)名词(从密码-客户0.38.

[注] 正在中断类型脚本更改 : encrypt编号 :encryptBulk 现在返回 EncryptedPayload (单位:千美元)Encrypted | EncryptedV3改为 Encrypted。 。 。 . V2 客户端的运行时输出没有变化, 但访问的代码 .k 或指定结果为 Encrypted 必须先缩小。 v3 分级带 没有 k在读它之前注意它的存在 'k' in payload && payload.k === 'ct' (a v2 scalar),或检查 payload.v === 3 以检测v3成员。 (赤裸的) payload.k === 'ct' 不汇编反对工会。 )

大缩略语

已加密 cast_as: 'bigint' 列存储已签名的64位整数 (PostgreSQL 存档副本) bigint) (中文(简体) ). encrypt 编号 : encryptBulk 编号 : encryptQuery 编号 : encryptQueryBulk 接受简写为JS number 或联合来文 bigint数字 :

  • bigint 输入是精确的,并对照完整的i64范围检查范围: -9223372036854775808至9223372036854775807 (2^63至2^63-1) (中文(简体) ). 区域外的值 RangeError 指定界限和界限 错误的方向。 没有 code 这是JS的边界检查, 而不是在下面描述的 Rust 错误之一 错误搜索索引 条款(hm, (中文(简体) ), ob, (中文(简体) ), op)由同值产生,所以边界 请检查是否也覆盖索引- 期限生成 .
  • number 输入保留现有的精确整数 :分数, 非无限值, 或超出范围值出错, 而不是沉默 被截断了.
  • A 类 bigint 仅被接受为平面图的顶层平面文字 栏。 JSON没有大块头,所以大块头都在里面 cast_as: 'json' 文档(或 JSON 封装查询条款) 以 TypeError 尼昂和原子弹上 更一般地说, 简洁的文字如下 JSON.stringify 两个平台上的语义: toJSON 获得荣誉(a) Date 成为它的ISO字符串, undefined 属性被丢弃,以及 非无限数字成为 null。 。 。 .
  • 内线形式 {"__protect_ffi_bigint__": "<digits>"} 实值 保留:a cast_as: 'json' 完全由这个构成的简单文字 单键形状(带有相距 i64 小数字符串) 读取为 a 比林特在边界,拒绝Json列。 嵌入 较大文档中键的发生不受影响.
const ciphertext = await addon.encrypt(client, {
  plaintext: 9007199254740993n, // beyond Number.MAX_SAFE_INTEGER — stays exact
  column: 'score',
  table: 'users',
})
const decrypted = await addon.decrypt(client, { ciphertext })
// decrypted === 9007199254740993n (a JS bigint)

[尖叫声] 中断更改 : decrypt 编号 : decryptBulk 编号 : decryptBulkFallible 现在,总是返回一个JS bigint 页:1 cast_as: 'bigint' 栏-甚至 符合联署材料编号的价值观。 以前的释放返回a number,无声地失去精确度 Number.MAX_SAFE_INTEGER (2^53-1) (中文(简体) ). 代码比较或计算已解密的大意 列必须更新(例如:. decrypted === 123n 改为 decrypted === 123,或 Number(decrypted) 当值已知时 (小一点).

错误

两个条目都投出一个普通的JS Error 拥有稳定 code,设置在 Rust 中 从错误变体。 没有包装课 也没什么可拆的.

并不是每次失败都有代码——包裹密码-客户端错误 到达没有田. 'UNKNOWN' 是那个案件的名称,而不是一个值 脏发作.

类型脚本类型 a catch 变量为 unknown,所以你缩小一次。 那个 此软件包不需要任何信息 :

try {
  await addon.encryptQuery(client, opts)
} catch (err) {
  if (err instanceof Error && 'code' in err && err.code === 'INVALID_JSON_PATH') {
    // handle JSON path mistakes
  }
  throw err
}

要将代码作为打入的值环绕, 请使用 isProtectErrorCode检查中 价值而不是外地存在的重要性: 节点集 code 独自一人 错误,所以赤裸裸 err?.code 读取会让 ECONNRESET 其中一个通过 这些.

import {
  isProtectErrorCode,
  type ProtectErrorCode,
} from '@cipherstash/protect-ffi'

function errorCode(err: unknown): ProtectErrorCode | undefined {
  const { code } = err as { code?: unknown }
  return isProtectErrorCode(code) ? code : undefined
}

密码本身是 PROTECT_ERROR_CODES 内 src/errors.ts,哪个 src/errorCodes.test.ts 检查 #[diagnostic(code(..))] 属性 Error 内 crates/protect-ffi/src/lib.rs - 一个地方一个代码 决定.

可用的脚本

在工程目录中,您可以运行:

由于这个包裹被吸收到 cipherstash/stack 单雷波, 货物被故意避免违约 build 财务报告和审定财务报表 test 路径: 根 pnpm test 到达每个包裹,在这条道路上的货运过程会给每个贡献者的机器上放上一个Rust工具链. 鲁斯特活佛以自己的剧本为后盾,以下列举. packages/protect-ffi/src/lintWiring.test.ts 执行分法.

pnpm run build

编译类型脚本 lib/ 与 tsc。这是软件包 main,它被生成——一个工作空间的消费者解决一个空包直到运行. 它不会建造土生土长的; 使用 build:native 对于这个.

pnpm run build:native

构建节点加通( N)index.node)来源,生成一个发布库 cargo --release。 。 。 .

额外 cargo build 可附上论据。 注意没有 -- 分隔符 – pnpm 向前 -- 逐字记录( npm 剥去它),这些脚本结束于 > cargo.log因此,一个分离的旗地 之后 重新定向和货物拒绝作为立场论据。 例如,允许 货物特性数字 :

pnpm run build:native --feature=beetle

pnpm run debug

类似 build:native 但生成调试构建 cargo。 。 。 .

pnpm run cross

类似 build:native 但使用时 交叉键 要交叉编译到另一个平台。 使用 CARGO_BUILD_TARGET 用于选择构建目标的环境变量. pnpm run zigbuild 通过 货物建造,这是释放矩阵用来给glibc打针.

pnpm run build:wasm

构建 wasm32-unknown-unknown 输出到 dist/wasm/ 与 wasm-pack,然后插入 .wasm 作为基准64 ./wasm-inline 输入。 需要原32目标 wasm-pack (两者均由 mise install 在此目录中).

pnpm test

类型检查TypeScript,运行单元测试(tsc + vitest),并用提示和格式检查TypeScript. 无货物,按设计——见上文.

注:这也没有进行整合测试. 对于这些,见 下级。 。 。 .

pnpm run test:cargo

测试套房的一分之一: cargo test --locked 加号 cargo fmt --check。运行时使用路径过滤的根工作流程,而不是 pnpm test。 。 。 . --locked 而不是悄悄地再生出一个僵尸 Cargo.lock ——建筑脚本没有通过,所以一个合法地更新锁的局部建筑仍然有效.

pnpm run test:typecheck:wasm

类型检查 type-tests/ 与原子弹产生的声明相对应 dist/wasm/需求 build:wasm 财务报告和审定财务报表 build 因此它不属于 pnpm test ——必须经过一个新的克隆人,没有 dist/。 。 。 .

mise run lint:rust

每个Rust检查CI门上:对宿主目标进行剪接,对接 wasm32-unknown-unknown,以及 cargo fmt --check。 。 。 . pnpm test 把这些都抛出,所以在推动rust改变之前先跑.

原臂需要安装一次目标:

rustup target add wasm32-unknown-unknown

mise run fmt 修正检查报告的格式.

项目布局

该项目的目录结构是:

protect-ffi/
├── Cargo.toml
├── README.md
├── integration-tests/
├── lib/
├── src/
|   ├── index.mts
|   └── index.cts
├── crates/
|   └── protect-ffi/
|       └── src/
|           └── lib.rs
├── platforms/
├── docs/
├── scripts/
├── package.json
└── target/
条目目的
Cargo.toml货物 显示文件,通知 cargo 命令.
README.md这个文件.
integration-tests/包含整合测试的目录.
lib/包含生成输出的目录 页:1。 。 。 .
src/包含 TypeScript 源文件的目录.
index.mts此库通过时的输入点 无害环境管理 import 语法.
index.cts此库通过时的输入点 司法理事会 require。 。 。 .
crates/包含项目 Rust 源代码的目录树 .
lib.rsRust源代码的入站点.
platforms/包含此库所支持的每个平台的二进制附加后端的分布的目录.
docs/JSONB 整合文件和移民说明.
scripts/构建帮助脚本( 如 WASM inlineing) .
package.json时间 显示文件,通知 npm 命令.
target/二进制由罗斯特建筑所出产的文物.

融合测试

融合测试现场 ./integration-tests 目录. 这些测试利用了 Rust 和 JavaScript 的本地建筑来测试 @cipherstash/protect-ffi 正如API消费者那样.

目录是它自己的私人 pnpm 工作空间成员(@cipherstash/ffi-integration-tests),这样 pnpm install 在回波根安装它, 两者 @cipherstash/protect-ffi 财务报告和审定财务报表 @cipherstash/eql 解决此树上的包而不是公布柏油丸。 它已经 没有 test 脚本 目标——根 pnpm test 否则会尝试运行一个套房 需要多克和现场证明。 使用 mise 任务.

这些测试依赖于:

  • 要配置的 CipherStash (通过) .toml 配置或环境变量),以及
  • 待设定的 Postgres 环境变量

示例环境变量:

CS_CLIENT_ID=
CS_CLIENT_KEY=
# The integration tests read CS_CLIENT_ACCESS_KEY; the library itself reads CS_ACCESS_KEY
CS_CLIENT_ACCESS_KEY=
CS_WORKSPACE_CRN=
# Must match the port used by the integration-test setup (mise.toml)
PGPORT=5436
PGDATABASE=cipherstash
PGUSER=cipherstash
PGPASSWORD=password
PGHOST=localhost

进行整合测试:

mise setup
mise test:integration

也可以以"观察"模式运行集成测试:

mise test:integration --watch

默认的锁上下文测试不包含在内,因为无效锁上下文在ZeroKMS中的火灾安全警告. 包括这些,运行:

mise test:integration:all

释放

这个包被吸收到 cipherstash/stack 和它的放出现在运行在该存储器的 更改 流动而不是a workflow_dispatch 触发。 不再有手取的版本: 包装及其6个平台软件包共享更改器 fixed 组,所以 释放所有7个在锁步.

写入更改列表( E)pnpm changeset 在 repo 根) 命名 @cipherstash/protect-ffi; 固定组向平台传播凸起 软件包。 变形体变成 CHANGELOG.md 条目 - 更改器拥有 现在的文件,所以 [Unreleased] 标题和 version npm 生命周期 用来宣传它的钩子不见了.

出版的剪接已经完成,随之而来的是更改的禁运. 无 现在所有七个软件包都值得信任的出版 cipherstash/stack绑定 改为 release.yml名称 @cipherstash/protect-ffi 释放 通常情况下。 挡住一个的警卫scripts/lint-no-ffi-changeset.mjs和 页:1 .changeset/<name>.md.deferred 将它停到切口的公约 都不见了.

而路径被证明, 不只是配置 —— 说 0.32.0 发表于 此仓库 。 Npm 名称上的 SLSA 出处 https://github.com/cipherstash/stack 财务报告和审定财务报表 .github/workflows/release.yml, (中文(简体) ), 这是解决问题的唯一证据: 这一段说,FFI的释放没有从这里流出,写着0.31.0 当0.32.0 降落时,最新鲜的 也从未重温过.

如果您发现一个文件仍然带有扩展名, 该文件是写在 封锁,目前为惰性而非停放: @changesets/read 选择在 .endsWith('.md'),因此它描述的更改会空出 更改日志条目和无内容 changeset version 或者说 changeset publish 将 说吧 重命名为 git mv,或者如果更改从未运出,则删除. scripts/__tests__/no-parked-changesets.test.mjs 不管是哪一种方式都失败了.

上一个存储器的 GitHub 动作工作流程保存在 .github/ 在此目录中由子树导入并保存为参考 那个港口 他们走了,六个平台的建构矩阵 现在生活在 仓库根 .github/workflows/_build-ffi-artifacts.yml,与 ffi-preflight.yml 作为它的人工分发干线。 没有包裹 目录由 GitHub 执行, 它从仓库读取工作流程 仅根- src/lintWiring.test.ts 如果有人回来了,就失败了.

学习更多

更多地了解:

版本与发布记录

@cipherstash/protect-ffi 共发布 50 个版本,首次发布于 2025/02/18;过去 12 个月发布了 22 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.33.0latest2026/10/021 个月前
0.32.02026/08/192 个月前
0.31.02026/07/273 个月前
0.30.02026/07/203 个月前
0.29.02026/07/104 个月前
0.28.02026/07/084 个月前

其他发布通道:next → 0.18.0-9。

关于 @cipherstash/protect-ffi 的常见问题

@cipherstash/protect-ffi 是什么?

原始 FFI 绑定到 CipherStash 客户端 SDK — 权限@ cipherstash/stack

如何安装 @cipherstash/protect-ffi?

在项目目录运行 npm install @cipherstash/protect-ffi,或使用 pnpm add / yarn add / bun add。

@cipherstash/protect-ffi 支持 TypeScript 吗?

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

@cipherstash/protect-ffi 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@cipherstash/protect-ffi 可以商用吗?

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

@cipherstash/protect-ffi 的下载量有多少?

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

@cipherstash/protect-ffi 还在维护吗?

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