22 KiB
22 KiB
@suaveplan/vault - Vault orchestration layer for the Vaulted password-manager ecosystem: drive-backed encrypted SQLite container, lifecycle state machine (open → unlock → lock), per-item field encryption, attachments / shares / sync delegation, and OpaqueKeyHandle boundary enforcement.
Summary
Vault orchestration layer for the Vaulted password-manager ecosystem. Provides a drive-backed encrypted SQLite-style container, a strict locked ↔ unlocked lifecycle state machine, per-item field encryption for concealed values, attachment / share / sync delegation behind injectable adapter interfaces, and OpaqueKeyHandle boundary enforcement so raw key bytes never leave @suaveplan/crypto-vault.
Documentation tree
- attachments — (no submodules)
- attachments — (no submodules)
- container — (no submodules)
- container — (no submodules)
- encryption — (no submodules)
- field-encryption — (no submodules)
- codes —
codes.tsdefines the stable error-code registry for@suaveplan/vault. Every failure raised by the orchestration lay… - vault-error —
vault-error.tsdefinesVaultError, the concreteSuaveplanErrorsubclass thrown by every public path in `@suavepla… - items — (no submodules) - migrations — _(no submodule…
- migrations — (no submodules)
- migrations — (no submodules)
- helpers —
items-helpers.tsextracts the pure transformation helpers used by the items repository: row-to-item projection (with… - items — (no submodules) - migrations — _(no submodule…
- migrations — (no submodules)
- manifest — (no submodules)
- manifest — (no submodules)
- otel — (no submodules)
- instruments — (no submodules)
- schemas — (no submodules)
- schemas — (no submodules)
- search — (no submodules)
- search — (no submodules)
- shares — (no submodules)
- shares — (no submodules)
- sync — (no submodules)
- sync — (no submodules)
- types — (no submodules)
- vault — (no submodules) - wiring —
wiring.tsextracts the per-unlock module…- lifecycle —
lifecycle.tsextracts the two side-effecting helpers that drive the unlock path:deriveVaultKeyBuffer(running 2SKD… - vault — (no submodules) - wiring —
wiring.tsextracts the per-unlock module… - wiring —
wiring.tsextracts the per-unlock module factories used byVault:buildItems,buildSearch,buildAttachments,…
- lifecycle —
- vault-error —
vault-error.tsdefinesVaultError, the concreteSuaveplanErrorsubclass thrown by every public path in `@suavepla… - items — (no submodules)- migrations — (no submodules)- migrations — (no submodules)- helpers —
items-helpers.tsextracts the pure transformation helpers used by the items repository: row-to-item projection (with…- items — (no submodules)- manifest — (no submodules)- manifest — (no submodules)- otel — (no submodules)- instruments — (no submodules)- schemas — (no submodules)- schemas — (no submodules)- search — (no submodules)- search — (no submodules)- shares — (no submodules)- shares — (no submodules)- sync — (no submodules)- sync — (no submodules)- types — (no submodules)- adapters —
adapters.tsdefines the Phase-1 adapter contracts the vault orchestration layer uses to talk to blob storage, share-l… - types — (no submodules)- vault — (no submodules)- wiring —
wiring.tsextracts the per-unlock module… - lifecycle —
lifecycle.tsextracts the two side-effecting helpers that drive the unlock path:deriveVaultKeyBuffer(running 2SKD… - vault — (no submodules)- wiring —
wiring.tsextracts the per-unlock module… - wiring —
wiring.tsextracts the per-unlock module factories used byVault:buildItems,buildSearch,buildAttachments,…
- items — (no submodules)- manifest — (no submodules)- manifest — (no submodules)- otel — (no submodules)- instruments — (no submodules)- schemas — (no submodules)- schemas — (no submodules)- search — (no submodules)- search — (no submodules)- shares — (no submodules)- shares — (no submodules)- sync — (no submodules)- sync — (no submodules)- types — (no submodules)- adapters —
- attachments — (no submodules)- container — (no submodules)- container — (no submodules)- encryption — (no submodules)- field-encryption — (no submodules)- codes —
codes.tsdefines the stable error-code registry for@suaveplan/vault. Every failure raised by the orchestration lay… - vault-error —
vault-error.tsdefinesVaultError, the concreteSuaveplanErrorsubclass thrown by every public path in `@suavepla… - items — (no submodules)- migrations — <!-- begin:auto…
- migrations — (no submodules)- migrations — (no submodules)- helpers —
items-helpers.tsextracts the pure transformation helpers used by the items repository: row-to-item projection (with… - items — (no submodules)- migrations — <!-- begin:auto…
- migrations — (no submodules)- migrations — (no submodules)- helpers —
- manifest — (no submodules)- manifest — (no submodules)- otel — (no submodules)- instruments — (no submodules)- schemas — (no submodules)- schemas — (no submodules)- search — (no submodules)- search — (no submodules)- shares — (no submodules)- shares — (no submodules)- sync — (no submodules)- sync — (no submodules)- types — (no submodules)- adapters —
adapters.tsdefines the Phase-1 adapter contracts the vault orchestration layer uses to talk to blob storage, share-l…- types — (no submodules)- vault — (no submodules)- wiring —
wiring.tsextracts the per-unlock module… - lifecycle —
lifecycle.tsextracts the two side-effecting helpers that drive the unlock path:deriveVaultKeyBuffer(running 2SKD… - vault — (no submodules)- wiring —
wiring.tsextracts the per-unlock module… - wiring —
wiring.tsextracts the per-unlock module factories used byVault:buildItems,buildSearch,buildAttachments,…
- types — (no submodules)- vault — (no submodules)- wiring —
- attachments — (no submodules)- container — (no submodules)- container — (no submodules)- encryption — (no submodules)- field-encryption — (no submodules)- codes —
codes.tsdefines the stable error-code registry for@suaveplan/vault. Every failure raised by the orchestration lay… - vault-error —
vault-error.tsdefinesVaultError, the concreteSuaveplanErrorsubclass thrown by every public path in `@suavepla… - items — (no submodules)- migrations — _(no submodul…
- migrations — (no submodules)- migrations — (no submodules)- helpers —
items-helpers.tsextracts the pure transformation helpers used by the items repository: row-to-item projection (with… - items — (no submodules)- migrations — _(no submodul…
- migrations — (no submodules)- migrations — (no submodules)- helpers —
- manifest — (no submodules)- manifest — (no submodules)- otel — (no submodules)- instruments — (no submodules)- schemas — (no submodules)- schemas — (no submodules)- search — (no submodules)- search — (no submodules)- shares — (no submodules)- shares — (no submodules)- sync — (no submodules)- sync — (no submodules)- types — (no submodules)- adapters —
adapters.tsdefines the Phase-1 adapter contracts the vault orchestration layer uses to talk to blob storage, share-l…- types — (no submodules)- vault — (no submodules)- wiring —
wiring.tsextracts the per-unlock module… - lifecycle —
lifecycle.tsextracts the two side-effecting helpers that drive the unlock path:deriveVaultKeyBuffer(running 2SKD… - vault — (no submodules)- wiring —
wiring.tsextracts the per-unlock module… - wiring —
wiring.tsextracts the per-unlock module factories used byVault:buildItems,buildSearch,buildAttachments,…
- types — (no submodules)- vault — (no submodules)- wiring —
- attachments — (no submodules)- container — (no submodules) - container — (no submodules)- encryption — (no submodules) - field-encryption — (no submodules)- codes —
codes.tsdefines the stable error-code registry for@suaveplan/vault. Every failure raised by the orchestration lay… - vault-error —
vault-error.tsdefinesVaultError, the concreteSuaveplanErrorsubclass thrown by every public path in `@suavepla… - items — (no submodules) - migrations — (no submodules) - migrations — (no submodules) - helpers —
items-helpers.tsextracts the pure transformation helpers used by the items repository: row-to-item projection (with…- items — (no submodules)- manifest — (no submodules) - manifest — (no submodules)- otel — (no submodules) - instruments — (no submodules)- schemas — (no submodules) - schemas — (no submodules)- search — (no submodules) - search — (no submodules)- shares — (no submodules) - shares — (no submodules)- sync — (no submodules) - sync — (no submodules)- types — (no submodules) - adapters —
adapters.tsdefines the Phase-1 adapter contracts the vault orchestration layer uses to talk to blob storage, share-l… - types — (no submodules)- vault — (no submodules) - lifecycle —
lifecycle.tsextracts the two side-effecting helpers that drive the unlock path:deriveVaultKeyBuffer(running 2SKD… - vault — (no submodules) - wiring —
wiring.tsextracts the per-unlock module factories used byVault:buildItems,buildSearch,buildAttachments,…
- items — (no submodules)- manifest — (no submodules) - manifest — (no submodules)- otel — (no submodules) - instruments — (no submodules)- schemas — (no submodules) - schemas — (no submodules)- search — (no submodules) - search — (no submodules)- shares — (no submodules) - shares — (no submodules)- sync — (no submodules) - sync — (no submodules)- types — (no submodules) - adapters —
Install
bun add @suaveplan/vault
Quick start
import { Vault } from "@suaveplan/vault";
const vault = Vault.open({
vaultId: "personal",
email: "user@example.com",
containerPath: "/personal.vault",
attachmentsPrefix: "/personal/attachments",
argon2id: { memoryKB: 65536, iterations: 3, parallelism: 4, outputLength: 32 },
argon2idSalt: argon2idSaltBytes,
drive,
driveSharing,
sync,
});
await vault.unlock(password, secretKey);
const item = vault.items.create({
title: "GitHub",
category: "login",
fields: { username: "alice", url: "https://github.com" },
concealedFields: { password: "p@ssw0rd" },
tags: ["work"],
favorite: true,
});
const fetched = vault.items.get(item.id);
const matches = vault.search.search({ query: "github", category: "login" });
await vault.lock(); // zeroizes all key material
Modules
vault/vault— orchestration entry point, lifecycle state machine.vault/lifecycle— 2SKD + key-hierarchy + drive load/create helpers.vault/wiring— module factory helpers.manifest/manifest— bitpacker-packed container manifest header.container/container— drive-backed encrypted blob layer.items/items— in-memory items repository.items/helpers— pure row projection / patch helpers.items/migrations/migrations— forward-only schema migrations.encryption/field-encryption— per-item field encryption.search/search— concealed-field-safe search.attachments/attachments— drive-backed attachment storage.shares/shares— share-link orchestration.sync/sync— sync delegation orchestrator.errors/codes— stableVAULT.*error code registry.errors/vault-error—VaultErrorclass.otel/instruments— OTel histogram + counter instruments.schemas/schemas— Zod schemas for every public boundary.types/types— orchestration-level types.types/adapters— Phase-1 adapter interfaces (DriveLike / DriveSharingLike / VaultSyncLike).
Security model
- 2SKD:
Argon2id(HMAC-SHA256(secretKey, password ‖ 0x00 ‖ email), salt, params)produces the AUK, which is HKDF-derived throughpersonal-keyset → vault-key → item-key. Intermediate buffers are zeroized eagerly. - OpaqueKeyHandle boundary: the vault key is wrapped in a
SecureBufferand accessed only through@suaveplan/crypto-vault'sunwrap— never as raw bytes across module boundaries. - Defence in depth: even if the drive-level AEAD is compromised, concealed fields (passwords, PINs, secure notes) remain encrypted with per-item keys inside the SQLite-style container.
- Manifest binding: the bitpacker-packed manifest header is bound as AAD to the container's AEAD, so any tampering invalidates the authentication tag.
zeroizeAll()on lock: every liveSecureBufferis overwritten with zeros and the references released.
Performance
- Unlock is dominated by Argon2id (≥ 64 MiB / 3 iterations / parallelism configurable). Default parameters ship at OWASP minimums.
- Items, search, attachments, and share operations are all in-memory after unlock; flushes are debounced (default 500 ms; configurable via
flushDebounceMs). - Drive I/O happens twice per unlock at most: once on read, once for the freshly-encrypted blob if creating a new vault. Ongoing operations only touch drive on flush + lock.
Phased delivery
This is the Phase-1 landing. Adapter interfaces (DriveLike, DriveSharingLike, VaultSyncLike) are defined inline; production wiring binds the real @suaveplan/drive, @suaveplan/drive-sharing, and @suaveplan/vault-sync packages without changes to this surface.