@cef-ai/vault-sdk
v0.5.1
Published
Client SDK for apps talking to a user's CEF Vault from outside the Agent Runtime: chat UIs, Slack bots, data pipelines, integrations.
Readme
@cef-ai/vault-sdk
Client SDK for apps talking to a user's CEF Vault from outside the Agent Runtime: chat UIs, Slack bots, data pipelines, integrations.
Install
pnpm add @cef-ai/vault-sdkQuickstart
Browser app driving onboarding through an embed-wallet:
import { VaultSDK, type EnsureProgressEvent } from "@cef-ai/vault-sdk";
const sdk = new VaultSDK({
endpoint: "https://vault.cere.io",
garEndpoint: "https://gar.cere.io",
rpcEndpoint: "wss://rpc.devnet.cere.network/ws",
s3GatewayAuthInfoUrl: "https://ddc-s3-gateway.compute.dev.ddcdragon.com/auth/info",
// Override to point at the prod marketplace; defaults to the dev cluster URL.
marketplaceEndpoint: "https://agent-marketplace.compute.dev.ddcdragon.com",
wallet: myWallet, // any `Wallet` adapter
embedWalletForOnboarding: myEmbedWallet, // optional; chain-side autoAddProxy
});
const vault = await sdk.vault.ensure({
onProgress: (e: EnsureProgressEvent) => console.log(e.kind),
});
await vault.scope("default").publish({
type: "user_message",
context: "conv-42",
payload: { text: "Hi" },
});Server-side (pre-provisioned wallet, skip onboarding):
import { VaultSDK, CereWallet } from "@cef-ai/vault-sdk";
const wallet = await CereWallet.fromMnemonic(process.env.WALLET_MNEMONIC!);
const sdk = new VaultSDK({
endpoint: "https://vault.cere.io",
garEndpoint: "https://gar.cere.io",
s3GatewayAuthInfoUrl: "https://ddc-s3-gateway.compute.dev.ddcdragon.com/auth/info",
wallet,
});
const vault = await sdk.vault.ensure({ onboard: false });Onboarding
vault.ensure() runs the DDC onboarding pipeline by default. It probes the
gateway, optionally bootstraps a devnet wallet, submits the on-chain
proxy.addProxy extrinsic, and polls until the gateway sees the proxy.
Internally:
- Status check —
GET /auth/onboarding/status(404 means prod, skip). - Bootstrap (devnet only) — if
faucet_eligibleorddc_seed_eligible,POST /auth/onboarding/bootstrapto drip CERE and seed the DDC deposit. - autoAddProxy — when the wallet is not yet a registered gateway proxy,
submit
proxy.addProxy(<gateway>, NonTransfer, 0)via the configuredembedWalletForOnboarding. - Poll-until-ready — re-fetch status until
gateway_is_proxy === trueor the time budget is exhausted (defaults: 2 s interval / 60 s budget).
onProgress receives an EnsureProgressEvent for each stage:
| Kind | Meaning |
| --- | --- |
| inspecting-wallet | Gateway status probe is in flight. |
| funding-wallet | Devnet auto-fund (CERE drip + DDC seed deposit); never fires on prod. |
| gateway-authorization-required | Onboarding required; carries status: OnboardingStatus. |
| authorizing-gateway | Wallet popup is open for the addProxy signature. |
| authorization-submitted | Extrinsic accepted into the mempool; carries txHash. |
| awaiting-chain-confirmation | Polling the gateway until it sees the proxy. |
| gateway-authorized | Gateway confirmed the proxy on-chain. |
| signing-delegation | Building the DDC delegation token from the wallet. |
| provisioning-vault | Calling POST /api/v1/vaults to claim the record. |
Two errors can surface:
OnboardingTimeoutError— the proxy poll exhausted its budget. The extrinsic may still confirm later; callingensure()again is safe.OnboardingRequiredError—ensure({ onboard: false })was passed but the gateway reports the wallet still needs the proxy step. Server flows catch this to fail fast.
@cere-ddc-sdk/blockchain (used by autoAddProxy) and
@cere-ddc-sdk/ddc-client (used to build the delegation token internally)
are declared as optionalDependencies. Apps that exercise the onboarding
or internal-token-build paths must install them as direct dependencies.
Connecting an agent
vault.agents.connect({ manifest, scope, settings }) is the high-level entry
point. It builds the v2 agreement payload, signs it with your wallet, POSTs
the signed envelope to the Global Agent Registry (GAR), and then calls
vault-api POST /agents with the resulting signature — all internally.
const manifest = await sdk.marketplace.getAgent("personal-assistant");
const handle = await vault.agents.connect({
manifest,
scope: "default",
settings: { language: "en" },
});
console.log(handle.agentId, handle.status);Requires garEndpoint and a wallet adapter on VaultSDKConfig. If you
already have a pre-signed agreementSignature (e.g. from a custodial flow),
drop down to the low-level Agents.connect(vaultId, ConnectAgentRequest)
exported from @cef-ai/vault-sdk/internal.
Surface
| Method | Maps to |
| --- | --- |
| sdk.vault.ensure({ onboard?, onProgress?, delegationToken? }) | runs the onboarding pipeline + POST /api/v1/vaults |
| sdk.vault.current() / .rotateCredentials() | GET / PUT /api/v1/vaults[/credentials] |
| vault.scopes.{list,get,create,update,delete} | /api/v1/vaults/:v/scopes |
| vault.agents.connect({ manifest, scope, settings }) (signs GAR internally) | POST {garEndpoint}/api/v1/agreements + POST /api/v1/vaults/:v/agents |
| vault.agents.{list,get} → AgentConnectionHandle | /api/v1/vaults/:v/agents |
| handle.update(settings) / handle.disconnect() | PATCH / DELETE |
| handle.cubby(alias).query(...) / .exec(...) | /scopes/:s/cubbies/:alias/{query,exec} |
| handle.jobs.list({ state?, cursor?, limit? }) | /agents/:a/jobs |
| vault.scope(s).publish(input) | POST /scopes/:s/events |
| vault.scope(s).subscribe(filter, handler, opts?) | poll-backed long-poll over /streams/:c/events |
| vault.scope(s).subscribeAll(filter, handler, { refreshIntervalMs? }) | fans out across streams.list(); refreshes every 30 s by default |
| vault.scope(s).streams.list() / .get(c) / .stream(c).events.list() | /scopes/:s/streams[/:c[/events]] |
| vault.jobs.list({ state? }) / .get(jobId).activities.list() | /jobs[/...] |
| handle.jobs.get(jobId).activities.subscribe({ since? }, handler) | poll-backed activity tail with seen-id dedupe |
| sdk.marketplace.getAgent(id, version?) | public GET /api/v1/marketplace/agents/:id[/versions/:v] |
| sdk.marketplace.list({ limit?, cursor?, query? }) | public GET /api/v1/marketplace/agents |
| sdk.health.live() / .ready() | public GET /health / /ready |
VaultSDKConfig knobs
| Field | Description |
| --- | --- |
| endpoint: string | Base URL for the vault API (wallet-authenticated). |
| garEndpoint?: string | Base URL for the Global Agent Registry. Required for vault.agents.connect. |
| marketplaceEndpoint?: string | Base URL for the agent marketplace API (separate host from the vault API; reads are unauthenticated). Default: dev cluster URL (https://agent-marketplace.compute.dev.ddcdragon.com). |
| s3GatewayAuthInfoUrl?: string | Gateway /auth/info URL used to fetch the gateway pubkey when s3GatewayPubkey is not set. Default: dev cluster gateway. |
| s3GatewayPubkey?: string | Static gateway pubkey; skips the /auth/info round-trip during vault.ensure(). |
| rpcEndpoint?: string | Cere chain WebSocket RPC URL used by the onboarding pipeline's autoAddProxy. Default: Cere devnet RPC. |
| wallet?: Wallet / auth?: AuthProvider | Authenticated calls. Mutually exclusive — auth overrides wallet. |
| embedWalletForOnboarding? | Cere embed-wallet for onboarding-driven proxy.addProxy extrinsics. |
| fetch?: typeof fetch | Inject a custom fetch for tests / non-browser runtimes. |
| timeoutMs?: number | Per-request timeout (default 30 s). |
Scope.metadata: Record<string, unknown> is a free-form, server-stored map.
vault.scopes.update(name, { metadata }) is a partial patch — server
semantics are shallow-merge per top-level key (keys in the patch overwrite,
others are preserved). The SDK whitelists the request body so only the keys
you supply reach the wire.
Pluggable auth
VaultSDK accepts either a wallet (default → WalletAuthProvider) or a
fully custom auth: AuthProvider:
new VaultSDK({ endpoint, auth: new BearerAuthProvider(token) });Ships with WalletAuthProvider and NoAuth. Implement AuthProvider
yourself to plug in delegation tokens, trusted-headers, or anything else.
Wallet adapters
The Wallet interface is { pubkey(), sign(bytes), sigType?() }.
KeypairWallet.fromSeed(bytes)— raw 32-byte ed25519 seed (server use).CereWallet.fromMnemonic(mnemonic, type?)— wrapsUriSigner.CereWallet.fromKeystore(json, passphrase, type?)— Cere/Polkadot JSON keystore.
Browser apps using @cere/embed-wallet should write a thin adapter
implementing the 3-method Wallet interface against the embed-wallet's
signer surface; the chat-app pattern at cef-chat/src/lib/wallet/embed-adapter.ts
is a working reference.
The UriSigner / JsonSigner classes powering CereWallet are vendored
from @cere-activity-sdk/signers
(Apache-2.0); see src/wallet/cere-internal/README.md for attribution and
divergences. We dropped the runtime dep because the upstream package is
neglected; the underlying @polkadot/keyring, @polkadot/util and
@polkadot/util-crypto are pulled in directly.
Browser-extension and CereWalletNative adapters live in their own packages.
Lower-level access
@cef-ai/vault-sdk/internal re-exports Vaults, Scopes, Agents,
Events, Browse, Cubbies, Health, Marketplace, plus
VaultHttpClient and endpoints.
Onboarding primitives (runOnboarding, pollUntilProxyReady,
getOnboardingStatus, runBootstrap, walletToOnboardingSigner),
autoAddProxy, and getGatewayInfo live under their respective files in
src/internal/ for advanced consumers that need to drive the pipeline
piecewise. Use them only when the fluent surface isn't enough.
Spec
See ../../specs/platform/03-components/vault-sdk.md (internal
sibling repo) for the design contract.
