All of this needs an unlocked Account.
Listing and opening
const vaults = await account.listVaults();
// [{ id, name, role: "read" | "write" | "manage", orgId, currentKeyVersion, pendingKeyVersion, rotationRequired }]
const vault = await account.openVault(vaults[0]!.id); // downloads and decrypts it
vault.name;
vault.db; // the kdbxweb database
vault.etag; // the server version this copy is based on
Names are decrypted on the device. openVault tries the newest key the account holds first and falls back
to the previous one, so a vault in the middle of a key rotation still opens.
Reading and writing fields
vault.read("ov://Team/GitHub/password");
vault.read({ groupPath: ["Apps"], entry: "Stripe", field: "username" });
await vault.write("ov://Team/GitHub/password", "new value"); // edits and syncs
await vault.write("ov://Team/Apps/Stripe/api_key", key, { create: true }); // creates the entry if needed
See Concepts for how references find entries. write keeps the previous
version in the entry’s history and stores password and custom fields protected.
Editing the database directly
vault.db is a kdbxweb Kdbx. Change it any way you like, then call
sync():
import { addEntryToDb, setItemFields } from "@openvireo/sdk";
const root = vault.db!.getDefaultGroup();
await addEntryToDb(vault.db!, root.uuid.toString(), {
title: "Database", username: "app", passwordValue: "s3cret", url: "", notes: "", tags: [],
});
setItemFields(vault.db!, { groupPath: ["Apps"], entry: "Api" }, { username: "svc", token: "…" }, { create: true });
const result = await vault.sync();
result.outcome; // "uploaded", "pulled" (only downloaded), "created" or "unchanged"
result.conflicts; // entries both sides changed: { uuid, kind, title, fields }
result.changes; // what the merge did, entry by entry
sync() downloads the server copy, merges it with your changes against the last common version, and
uploads the result with If-Match. When another device uploaded in between, it merges again and retries (up
to five times, then too_many_conflicts; your changes stay in vault.db). See
Merge behaviour.
Call vault.sync({ localChanged: false }) to only pick up changes from elsewhere: it asks the server with a
HEAD first and downloads nothing when the version hasn’t moved.
Creating vaults
const personal = await account.createVault("Personal");
const team = await account.createVault("Team Infra", { orgId }); // org owners and admins get manage automatically
createVault makes the vault key, registers the vault with the account server, and uploads an empty database
whose root group has the vault’s name. It returns the opened vault.
Sharing
const { principal, fingerprint } = await account.share(vaultId, { email: "[email protected]" }, "write");
await confirmWithBob(fingerprint); // compare it with what Bob's app shows, out of band
await account.share(vaultId, { principal }, "read"); // changing the role
share looks up the person’s public keys, checks their signature, seals the vault key to them and grants the
role. It needs manage on the vault. The other person needs an account already.
Revoking and rotating
await account.revoke(vaultId, [bobPrincipal]); // removes access and rotates the vault key in one step
await account.rotate(vaultId); // a new key without removing anyone
Rotating makes a new vault key, seals it to everyone who stays, re-encrypts the file with it and then makes it
current. Access is removed on the server first, so the removed person can’t download the re-encrypted
file. Their old downloads still open with the old key, so change the secrets inside if that matters. A vault
whose rotationRequired is true (someone left the organisation) should be rotated by a manager.
Organisations
Organisations are managed through the account server API on account.auth, with helpers on Account for
the steps that need keys:
const org = await account.auth.createOrg("Acme");
await account.auth.invite(org.id, { email: "[email protected]", role: "member" });
// Carol gets an email, and her app calls account.auth.acceptInvitation(invitationToken)
const { members } = await account.auth.listMembers(org.id);
const carol = members.find((m) => m.email === "[email protected]" && m.status === "accepted")!;
await account.confirmMember(org.id, carol, [{ id: "<vault id>", role: "read" }]); // seals vault keys to her
await account.setMemberRole(org.id, carol, "admin"); // admins get manage on every org vault
const { rotated, failed } = await account.removeMember(org.id, carol.user_id); // and rotates her vaults
(await account.auth.me()).orgs lists the organisations the account belongs to.
The SDK docs cover OpenVireo SDK, develop (86e6cd2), last updated 26 October 2026. Found a mistake? Edit this page on GitHub.