Vaults API
CRUD operations for encrypted password vaults.
Vault Structure
All vault data stored on the server is encrypted:
interface VaultResponse {
encryptedData: string; // AES-GCM encrypted entries (Base64)
iv: string; // Encryption IV (Base64)
wrappedVaultKey: string; // KEK-wrapped vault key (Base64)
vaultKeyIv: string; // Vault key wrap IV (Base64)
vaultKeySalt: string; // PBKDF2 salt (Base64)
version: number; // Optimistic locking version
updatedAt: string; // ISO 8601 timestamp
}Endpoints
List Vaults
GET /vault
Authorization: Bearer <token>{
"vaults": [
{
"name": "default",
"version": 5,
"updatedAt": "2024-01-15T10:30:00Z"
},
{
"name": "work",
"version": 12,
"updatedAt": "2024-01-14T15:45:00Z"
}
]
}Create Vault
POST /vault
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "personal",
"encryptedData": "base64-encrypted-entries",
"iv": "base64-iv",
"wrappedVaultKey": "base64-wrapped-key",
"vaultKeyIv": "base64-key-iv",
"vaultKeySalt": "base64-salt"
}{
"name": "personal",
"version": 1,
"createdAt": "2024-01-15T12:00:00Z"
}Get Vault
GET /vault/:name
Authorization: Bearer <token>{
"encryptedData": "base64-encrypted-entries",
"iv": "base64-iv",
"wrappedVaultKey": "base64-wrapped-key",
"vaultKeyIv": "base64-key-iv",
"vaultKeySalt": "base64-salt",
"version": 5,
"updatedAt": "2024-01-15T10:30:00Z"
}Update Vault
PUT /vault/:name
Authorization: Bearer <token>
Content-Type: application/json
{
"encryptedData": "base64-new-encrypted-entries",
"iv": "base64-new-iv",
"version": 5
}Important: Include the current version for optimistic locking.
{
"version": 6,
"updatedAt": "2024-01-15T12:30:00Z"
}Delete Vault
DELETE /vault/:name
Authorization: Bearer <token>{
"success": true
}Version Conflicts
Vault updates use optimistic locking to prevent data loss:
PUT /vault/:name
Content-Type: application/json
{
"encryptedData": "...",
"iv": "...",
"version": 5 // Must match current server version
}{
"error": "Version conflict",
"code": "VERSION_CONFLICT",
"currentVersion": 6,
"yourVersion": 5
}- Fetch current vault data
- Merge changes locally
- Retry with correct version
Client-Side Encryption Flow
Creating a New Vault
import { generateVaultKey, wrapVaultKey, encryptWithVaultKey } from "@pwm/shared";
// 1. Generate random vault key
const vaultKey = await generateVaultKey();
// 2. Wrap vault key with master password
const { wrappedKey, iv: vaultKeyIv, salt } = await wrapVaultKey(
vaultKey,
masterPassword
);
// 3. Encrypt empty vault
const vault = { entries: [] };
const { encryptedData, iv } = await encryptWithVaultKey(vault, vaultKey);
// 4. Send to server
await api.vault.$post({
json: {
name: "default",
encryptedData,
iv,
wrappedVaultKey: wrappedKey,
vaultKeyIv,
vaultKeySalt: salt
}
});Unlocking a Vault
import { unwrapVaultKey, decryptWithVaultKey } from "@pwm/shared";
// 1. Fetch encrypted vault
const response = await api.vault[":name"].$get({ param: { name: "default" } });
// 2. Unwrap vault key with master password
const vaultKey = await unwrapVaultKey(
response.wrappedVaultKey,
response.vaultKeyIv,
response.vaultKeySalt,
masterPassword
);
// 3. Decrypt entries
const vault = await decryptWithVaultKey(
response.encryptedData,
response.iv,
vaultKey
);
// vault.entries is now decryptedSaving Changes
// 1. Encrypt updated vault
const { encryptedData, iv } = await encryptWithVaultKey(vault, vaultKey);
// 2. Update on server (include version!)
await api.vault[":name"].$put({
param: { name: "default" },
json: {
encryptedData,
iv,
version: currentVersion
}
});Error Responses
Vault Not Found
{
"error": "Vault not found",
"code": "NOT_FOUND"
}Vault Already Exists
{
"error": "Vault already exists",
"code": "ALREADY_EXISTS"
}Cannot Delete Default Vault
{
"error": "Cannot delete default vault",
"code": "FORBIDDEN"
}Related
- Security: Encryption - How vault encryption works
- Sharing API - Share vaults with others
- CLI: Entries - Manage vault entries