Vault 加密小数据
用按 Plugin 隔离的 KV、文档和小型二进制对象保存加密状态。
Vault 是一项需要宿主显式启用的运行时能力,为每个 Plugin 提供相互隔离、加密持久化的 KV、文档和小型二进制对象。它适合保存 token、checkpoint、小配置和少量领域状态,但不能替代关系数据库或对象存储。
何时选择 Vault
| 数据 | 选择 |
|---|---|
| token、cursor、checkpoint、少量加密 JSON | Vault |
| 需要 query、index、join、migration 的结构化数据 | 数据库 |
| 大文件、用户上传和远端对象 | 对象存储(仓库内 S3 预览) |
| 进程内/跨实例短期加速 | 缓存(仓库内预览) |
Vault 只在 host 把 vault 配置为对象时安装;omitted 或 false 时没有 capability property、backend、preflight 或管理成本。
启用入口
需要使用 Vault 的宿主在启动配置中显式启用:
configure: () => ({
vault: {},
})配置对象承载 Vault 的 lifecycle 输入;导入某个 module 不会修改 Context plan。可以在无 Vault 宿主中运行的通用 Plugin
必须处理 capability absence;强依赖 Vault 的 Plugin 应在 init() 入口给出明确错误。
一个 namespace,三种视图
import { , } from '@pluxel/runtime'
@({ : 'Connector' })
export class extends {
protected override async () {
const = this..
if (!) throw new ('ConnectorPlugin requires host config vault: {}')
const = .()
const = .()
const = .().<{ : number; : number }>('cursors')
const = .().('client-certificate')
await .('access-token', 'secret')
await .('events', {
: 42,
: .(),
})
await .('certificate text')
await .()
}
}namespace() 默认返回当前 Plugin owner 的稳定 namespace。不同 Plugin 即使使用同一个 key 或 collection name,也不会进入同一默认 namespace。
不要把 namespace().name 当业务 identity 或对外 API;它由 runtime owner address 派生。
KV
const vault = this.ctx.vault
if (!vault) throw new Error('This Plugin requires host config vault: {}')
const kv = vault.kv()
await kv.set('token', token)
const current = await kv.get<string>('token')
await kv.setMany({ cursor: '42', region: 'hk' })
const entries = await kv.entries<string>()需要原子 read-modify-write 时使用 batch():
await kv.batch((tx) => {
const current = Number(tx.get<number>('attempts') ?? 0)
tx.set('attempts', current + 1)
})batch callback 操作内存中的 copy-on-write transaction,不在其中执行网络请求或长时间异步工作。
Documents
const vault = this.ctx.vault
if (!vault) throw new Error('This Plugin requires host config vault: {}')
const profiles = vault.docs().collection<{ enabled: boolean; label?: string }>('profiles')
await profiles.set('default', { enabled: true })
await profiles.patch('default', { label: 'Primary' })
const profile = await profiles.get('default')
const all = await profiles.list()documents 是按 ID 读取的小型 JSON records,没有 query planner、secondary index 或 migration engine。出现扫描、筛选、关联和 schema evolution 需求时迁移到数据库。
Blobs
const vault = this.ctx.vault
if (!vault) throw new Error('This Plugin requires host config vault: {}')
const blob = vault.blobs().open('oauth-state')
await blob.writeText(serialized)
const restored = await blob.readText()
await blob.remove()blobs 保存在 Vault snapshot 管理的文件区域,适合小型加密字节。大对象、流式上传、range request 和跨服务共享使用对象存储。
describe().path 只用于 server-side diagnostics,不暴露到 browser contract 或业务 API。
跨 KV 与 documents 的原子更新
稳定 namespace facade 支持一次更新 KV 和 documents:
const vault = this.ctx.vault
if (!vault) throw new Error('This Plugin requires host config vault: {}')
const space = vault.namespace()
await space.batch((tx) => {
tx.kv.set('cursor', 43)
tx.docs.collection<{ processed: boolean }>('events').set('43', {
processed: true,
})
})blob I/O 不进入这个 transaction。需要数据库级 durability、并发隔离或 outbox 时使用 database transaction。
Unlock 与失败语义
Vault 不在普通 Plugin 调用时偷偷 auto-unlock。host 在启动/preflight 阶段通过 host identity 或部署环境中的 age identity 解锁;若 storage 尚未 ready,Plugin 访问会 fail fast。
默认部署 identity 环境变量是 PLUXEL_VAULT_DEPLOY_IDENTITY,host 可通过 vault.deployIdentityEnv 改名。私钥不得写进普通 Plugin config、日志、Workbench DTO/API 或发行物。
vaultAdmin 是仅在 Vault enabled 时存在的 root-owned 宿主管理 API,用于 preflight、unlock、rekey 和 deploy recipient
管理。宿主读取前也必须检查 absence;业务 Plugin 只使用已检查的 ctx.vault,不调用 root admin API。
官方 @pluxel/auth 也是普通 Vault consumer。它只保存 password verifier、TOTP secret/last accepted counter,以及 confidential
OIDC client secret;plaintext password、生成的 OTP、session token、OIDC state/nonce/PKCE 和 rate-limit state 都只存在于请求或有界的
generation memory。凭据更新会在 provider ready snapshot 切换前显式 flush()。因此使用 local account 或 confidential OIDC client 的
host 必须配置 vault: {} 并在 Plugin lifecycle 前完成正常 preflight。
Flush 与 durability
写入会进入内存状态并按 host debounce 策略持久化。需要在关键边界确认 snapshot 已落盘时调用:
const vault = this.ctx.vault
if (!vault) throw new Error('This Plugin requires host config vault: {}')
await vault.flush()不要在每次高频状态变化后强制 flush;批量 checkpoint 或 shutdown 边界更合适。host 可通过 vault.flushDebounceMs 控制后台合并窗口。
安全检查
写入测试值并 flush() 后,正常停止并重启宿主,确认能读回相同值;再用另一个插件读取同名 key,确认默认 namespace 相互隔离。解锁失败应在宿主启动阶段处理,业务请求不会自动解锁。测试内容使用非敏感值,具体测试宿主见 测试插件。
最后更新于