Keep your secrets truly secret. With encrypted names and values, zero friction, and strict security by default, secrets‑engine gives developers defense‑in‑depth without the hassle. It’s a TypeScript SDK for secure secret storage, powered by machine‑bound AES‑256‑GCM and hardened SQLite.
- Zero friction — No passphrase, no setup wizard. Works out of the box.
- Maximum privacy — Both key names and values are encrypted. No metadata leakage.
- Machine-bound — Encryption keys are derived from machine identity + random keyfile via scrypt.
- Defense in depth — Filesystem permission verification, HMAC integrity checks, per-entry unique IVs.
- Actionable recovery — Static reset/destroy APIs recover unreadable stores without a successful
open(). - Bun-native — Built on
bun:sqliteand Node crypto. Zero external runtime dependencies.
bun add @wgtechlabs/secrets-engineimport { SecretsEngine } from "@wgtechlabs/secrets-engine";
// Open or create a store (defaults to ~/.secrets-engine/)
const secrets = await SecretsEngine.open();
// Store secrets with dot-notation namespacing
await secrets.set("openai.apiKey", "sk-...");
await secrets.set("anthropic.apiKey", "sk-ant-...");
// Retrieve
const key = await secrets.get("openai.apiKey"); // "sk-..."
// Check existence (no decryption needed — HMAC lookup)
await secrets.has("openai.apiKey"); // true
// List keys with glob patterns
await secrets.keys("openai.*"); // ["openai.apiKey"]
// Delete
await secrets.delete("openai.apiKey");
// Clean up
await secrets.close();The SDK resolves the storage directory using this priority order:
| Priority | Option | Path |
|---|---|---|
| 1 (highest) | { path: "/custom/path" } |
Explicit path |
| 2 | { location: "xdg" } |
~/.config/secrets-engine/ |
| 3 (default) | (none) | ~/.secrets-engine/ |
// XDG-aware
const secrets = await SecretsEngine.open({ location: "xdg" });
// Custom path
const secrets = await SecretsEngine.open({ path: "/opt/myapp/secrets" });Open or create a secrets store. Returns a Promise<SecretsEngine>.
Destroy a store at a path without requiring a successful open() first. For safety, this only deletes directories that look like a secrets-engine store.
Parameters:
optionsis optional and uses the same shape asSecretsEngine.open(options?)pathcan point to an explicit storage directory and takes precedence when providedlocationcan be used instead ofpathto resolve a preset storage location such as"xdg"- if
optionsis omitted, path resolution follows the same defaults asopen()described in Storage Location - there is no extra
forceflag; deletion is recursive only inside the validated store directory
Remove a store's contents and immediately recreate a fresh empty store at the same path. For safety, this only deletes directories that look like a secrets-engine store.
Parameters:
optionsis optional and includes the same base fields asSecretsEngine.open(options?)pathcan point to an explicit storage directory and takes precedence when providedlocationcan be used instead ofpathto resolve a preset storage location such as"xdg"preserveDirectorydefaults totrue, which removes the store contents but keeps the storage directory so the store can be recreated in place- if
optionsis omitted, path resolution follows the same defaults asopen()described in Storage Location - there is no extra
forceflag; reset removes store contents recursively only inside the validated store directory before reopening it
Retrieve a decrypted secret value. Returns string | null.
Retrieve a decrypted secret, throwing KeyNotFoundError if missing.
Store an encrypted secret.
Check if a key exists via HMAC hash lookup (no decryption).
Remove a secret. Returns true if deleted, false if not found.
List all key names, optionally filtered by glob pattern (e.g., "openai.*").
Irreversibly delete the entire store, keyfile, and directory.
Close the database connection and release resources. This method is async and must be awaited.
Returns a Promise<void> that resolves when the database is closed and integrity is finalized.
Breaking Change (v2.0.0): This method is now async. Update your code to await secrets.close().
Number of secrets currently stored.
Absolute path to the storage directory.
| Layer | Protection |
|---|---|
| Encryption | AES-256-GCM with unique IV per entry |
| Key derivation | scrypt (N=2¹⁷, r=8, p=1) from machine ID + random keyfile |
| Key name privacy | Both names and values encrypted; HMAC-SHA256 index |
| File permissions | Strict verification on open (700/600/400) |
| Integrity | HMAC-SHA256 of database contents in meta.json |
| Machine binding | Hostname + sorted MAC set + username + random keyfile |
Machine binding compatibility guarantees:
- new stores use a canonical sorted MAC-set binding, so adapter ordering changes do not change the derived identity
- older stores remain compatible through legacy single-MAC fallback candidates when the original MAC is still present
- stores that already include
machineBindingmetadata surfaceMACHINE_IDENTITY_CHANGEDwhen the canonical identity truly changes - legacy stores without
machineBindingmetadata still open normally, but incompatible identity changes fall back to the genericINTEGRITY_MISMATCHsubcode
| Error | When |
|---|---|
SecurityError |
File permissions too permissive |
IntegrityError |
Metadata, machine binding, or database HMAC verification fails |
KeyNotFoundError |
getOrThrow() for missing key |
DecryptionError |
Corrupted entry or wrong key |
InitializationError |
Cannot create store directory |
All errors extend SecretsEngineError with a .code property.
IntegrityError keeps code === "INTEGRITY_ERROR" and adds a machine-readable .subcode such as:
METADATA_MISSINGMETADATA_CORRUPTEDDATABASE_MISSINGUNSUPPORTED_VERSIONINTEGRITY_MISMATCHMACHINE_IDENTITY_CHANGEDCHECKPOINT_FAILED
bun install
bun test
bun run typecheck
bun run lintMIT — WG Tech Labs