Work with the Vault SDK
@cef-ai/vault-sdk is the client for talking to a vault from outside an
agent: a web or mobile app, a console, a server job. It signs requests, handles
canonicalization, and wraps the vault’s API in typed handles. It runs in the
browser and in Node.
Code running inside an agent uses ctx from @cef-ai/agent-sdk
(ctx.vault, ctx.cubby, ctx.memory, ctx.models), not this SDK.
Install
npm install @cef-ai/vault-sdk@5.5.01. Construct the client
import { VaultSDK, CereWallet } from "@cef-ai/vault-sdk";
const sdk = new VaultSDK({ endpoint: config.vaultUrl, // vault API garEndpoint: config.garUrl, // agreement registry; needed for agents.connect chainUrl: config.chainUrl, // needed for vault.ensure() with a signer signer: await CereWallet.fromMnemonic(mnemonic),});| Field | Required | Purpose |
|---|---|---|
endpoint |
yes | Vault API base URL. |
signer |
one of signer / auth |
Signs every request with the person’s key. |
auth |
one of signer / auth |
A custom auth provider, e.g. new DelegationAuthProvider(token). Overrides signer. |
garEndpoint |
for agents.connect |
Agreement registry the connect flow submits the signed agreement to. |
chainUrl |
for vault.ensure() with a signer |
Used to grant the vault service write delegation before the vault is claimed. Can also be passed per call. |
s3GatewayAuthInfoUrl |
Storage gateway the onboarding check talks to. | |
marketplaceEndpoint |
Marketplace API for sdk.marketplace reads. |
|
fetch, timeoutMs |
Custom fetch; per-request timeout (default 30 s). |
Signers
| Environment | Signer |
|---|---|
| Browser or mobile, from a mnemonic | await CereWallet.fromMnemonic(mnemonic) |
| From a JSON keystore | await CereWallet.fromKeystore(json, passphrase) |
| Server, from a raw 32-byte ed25519 seed | await KeypairWallet.fromSeed(seed) |
| Acting on someone’s behalf without their key | auth: new DelegationAuthProvider(token) |
Any object implementing the Signer interface (type, address, publicKey,
isReady(), sign(bytes)) works.
2. Open a vault
const vault = await sdk.vault.ensure({ name: "My Vault", onProgress: (e) => console.log(e.kind),});ensure() is idempotent: it returns the signer’s vault, claiming it on first
use. With a signer it checks onboarding status, grants the vault service write
delegation (this needs chainUrl), then claims the vault.
onProgress kind |
Meaning |
|---|---|
inspecting-wallet |
Onboarding status check in flight. |
funding-wallet |
Development networks only: the wallet is being funded. |
gateway-authorization-required |
The wallet still needs its storage-gateway authorization. ensure() throws OnboardingRequiredError next. |
authorizing-vault |
Granting the vault service write delegation. |
provisioning-vault |
Claiming the vault. |
On OnboardingRequiredError, authorize the gateway with
@cef-ai/account’s provisioning.ensure({ signer, chainUrl, gateway }), then
call ensure() again.
Other ways to open a vault:
| Call | Returns |
|---|---|
sdk.vault.current() |
Your own vault, without onboarding. |
sdk.vault.byId(vaultId) |
A vault you are a member of. |
3. Scopes
await vault.scopes.create({ name: "health", displayName: "Health" });const scopes = await vault.scopes.list();vault.scopes also has get(name), update(name, patch), and delete(name).
See Vaults for naming rules.
4. Publish and follow events
const scope = vault.scope("default");
const { eventId } = await scope.publish({ type: "user_message", context: "thread-123", payload: { text: "hi" },});
const sub = scope.subscribe( { context: "thread-123", types: ["reply"] }, (event) => console.log(event.type, event.payload),);// latersub.unsubscribe();await sub.closed;publishsends one event and throws if the vault rejects it.roledefaults touser;timestampdefaults to now.subscribepolls (default every 1000 ms, 100 events per page), de-duplicates by event id, and reports poll errors toonError. SetmaxBackoffMsto back off while polls fail.subscribeAll({ types }, handler)follows every stream in the scope and picks up new ones (every 30 s by default;refreshIntervalMs).scope.streams.list()andscope.stream(context).events.list({ cursor, limit })page history.
To start a workflow from your app, publish workflow.start with
target: "<agentServicePubkey>:<workflowId>".
5. Store objects
const res = await scope.objects.upload("notes/2026-10-08.txt", bytes, { contentType: "text/plain",});const data = await scope.objects.get(res.vaultPath);scope.objects also has head, presignedUrl(path, { ttlSeconds }), list({ prefix }), and delete.
Pass publishEvent: { type, context, payload } to upload to announce the
object in the same call; the event’s payload gets vaultPath.
6. Connect an agent
const connection = await vault.agents.connect({ agentId: "<agentServicePubkey>:<alias>", scope: "default", // or scopes: ["support", "sales"] settings: { language: "en" },});console.log(connection.status); // "provisioning" → "active"connect builds one agreement for the scope set, signs it, submits it to the
agreement registry, and creates the connection. It needs garEndpoint and a
signer. Optional fields: ceiling ({ gpuUnits?, a2aTokens? }) and
bundleCid (the bundle the person reviewed). See
Connections and consent.
| Call | Does |
|---|---|
vault.agents.list(), vault.agents.get(agentId) |
Read connections. |
connection.update(settings) |
Change the settings values. |
connection.setCeiling({ gpuUnits, a2aTokens }) |
Change the spend ceiling; {} clears it. |
connection.disconnect() |
Remove the connection; cubby data is kept. |
7. Read cubbies
A service’s cubbies are global within the vault. Address them by the service’s public key:
const { columns, rows } = await vault .service(agentServicePubkey) .cubby("history") .query("SELECT text, ts FROM messages ORDER BY ts DESC LIMIT ?", [20]);.exec(sql, params) writes. vault.service(pubkey).list() lists the service’s
cubbies.
8. Read the Memory Bank
const page = await vault.memory.search("refund approval", { scope: "finance", limit: 20 });Also list(type?, opts), get(id), neighbours(id, opts), and
countByType(opts). Results are { columns, rows, cursor? }; pass cursor back
for the next page. Writes are agent-only. See Memory Bank.
9. Follow runs
const jobs = await vault.jobs.list({ limit: 20 });const job = vault.jobs.get(jobs.items[0].jobId);const tasks = await job.tasks.list();const logs = await job.tasks.logs(tasks.items[0].taskId, { limit: 100 });connection.jobs.list() narrows to one agent. job.tasks.subscribe({ since }, handler)
follows new tasks.
Handle errors by code
import { VaultRequestError, BundleChangedError } from "@cef-ai/vault-sdk";
try { await vault.agents.connect({ agentId, scope: "default" });} catch (e) { if (e instanceof BundleChangedError) { // show the person the current bundle and ask again } else if (e instanceof VaultRequestError && e.code === "AGENT_ALREADY_CONNECTED") { // reuse the existing connection } else { throw e; }}| Error | Meaning |
|---|---|
VaultRequestError |
Any error from the vault; read .code, .status, .retryable. |
BundleChangedError, ReconsentRequiredError |
The connect’s bundle consent does not match. |
VaultSignerRequiredError |
The call needs a signer and none was configured. |
VaultNotImplementedError |
The vault answered 501. |
OnboardingRequiredError |
The wallet needs gateway authorization before ensure(). |
OnboardingTimeoutError |
Onboarding did not finish in time. |