# Secret Manager Convex component for encrypted secret vaults and issued API key lifecycle This is an upstream package. It has not been adapted or qualified for Bijection. - Package: convex-secret-manager - Source: https://github.com/phantasy-bot/convex-secret-manager - npm: https://www.npmjs.com/package/convex-secret-manager - Directory: https://bijection.com/components/convex-secret-manager Read the upstream README for setup, license, and compatibility before using it. ## Upstream package documentation Recorded 2026-10-23. This describes convex-secret-manager; check the Bijection adaptation separately. Secret Manager is a Convex component that combines encrypted secret vaults and issued API key lifecycle management into a single package with per-ownerId tenancy. The vault module stores third-party credentials encrypted at rest using envelope encryption with KEK rotation support, while the issued keys module handles machine token creation, validation, refresh, revocation, and audit trails. It targets multi-tenant backends where a single owner entity needs to hold both external credentials and issued machine tokens. ### Use cases - Storing per-organization API keys (OpenAI, Venice, webhook secrets) encrypted at rest with KEK rotation support for a SaaS multi-tenant backend - Issuing and managing machine tokens with hash-only storage, grace-period rotation, and idle/expiry sweeps for agent or service-to-service authentication - Building a unified secrets layer for a multi-tenant app where each tenant (org, user, or deployment) needs isolated credential namespaces - Auditing access and lifecycle events for both stored secrets and issued keys via a paginated audit trail - Replacing two separate gaganref packages (convex-secret-store and convex-api-keys) when one owner must hold both third-party credentials and issued tokens ### How it works Configuration starts in `convex/convex.config.ts` where you call `defineKeys` to supply versioned key-encryption-key (KEK) material via environment variables, then attach the component with `app.use(secretManager, ...)`. A `SecretManager` instance is constructed in your Convex module file by passing `components.secretManager`, giving you access to both the vault and issued keys APIs. The vault module exposes `vault.putPlaintext` for envelope-encrypted writes with additional authenticated data (AAD), `vault.getResult` for safe reads returning a `{ ok, value }` discriminated union, `vault.update` for metadata and TTL changes without re-encryption, and `vaultRotate.rotate` with `isRotationComplete` for draining KEK rotations. Secrets are scoped by a three-part path: `ownerId`, `namespace`, and `name`. The issued keys module provides `issued.create`, `issued.validate` (a side-effect-free query), `issued.touch`, `issued.revoke`, `issued.revokeAll`, and `issued.refresh` for grace-period token rotation. Keys use `sm_` prefixed tokens with hash-only storage. Both modules include built-in hourly cron sweeps for expired and idle records, plus `cleanupKeys`, `cleanupEvents`, and `auditEvents.listEvents` for operational maintenance and audit review. ### Upstream README # convex-secret-manager Convex component for **encrypted secret vaults** and **issued API key lifecycle**. Combines the roles of [gaganref/convex-secret-store](https://www.convex.dev/components/convex-secret-store) and [gaganref/convex-api-keys](https://www.convex.dev/components/convex-api-keys) into one package with per-`ownerId` tenancy. Any Convex app can use it (SaaS orgs, agents, multi-tenant backends). ## Two modules | Module | Use | |--------|-----| | **Vault** | Store user-supplied credentials (OpenAI, Venice, webhooks) encrypted at rest | | **Issued keys** | Issue `sm_` machine tokens with hash-only storage, refresh, revoke, and audit | ## vs gaganref | | gaganref (2 packages) | `convex-secret-manager` | |--|----------------------|-------------------------| | Path model | `namespace` + `name` | `ownerId` + `namespace` + `name` | | Use case | Generic apps | Multi-tenant backends that issue keys too | | Install | Two components | One component | | Vault crypto | Envelope + KEK rotation | Envelope (`defineKeys`) + legacy single-key | | Issued validate | Query (side-effect free) | Query | | Sweeps | Hourly crons | Hourly crons (built-in) | Use gaganref when you need only one concern. Use this package when one owner should hold both third-party credentials and issued machine tokens. ## Install ```bash npm install convex-secret-manager ``` ```ts // convex/convex.config.ts import { defineApp } from "convex/server"; import { v } from "convex/values"; import { defineKeys } from "convex-secret-manager"; import secretManager from "convex-secret-manager/convex.config.js"; const app = defineApp({ env: { MY_APP_KEK_V1: v.string(), }, }); app.use(secretManager, { env: { SECRET_MANAGER_KEYS: defineKeys({ 1: process.env.MY_APP_KEK_V1!, }), }, }); export default app; ``` ```ts // convex/secrets.ts import { SecretManager } from "convex-secret-manager"; import { components } from "./_generated/api.js"; export const secretManager = new SecretManager(components.secretManager); ``` Set on the Convex deployment: ```env SECRET_MANAGER_KEYS=1: # or legacy single key: SECRET_MANAGER_ENCRYPTION_KEY=... ``` ## Vault paths ```text ownerId = orgId | userId | deployment namespace = providers | integrations | webhooks name = openai.apiKey ``` ## Issued keys API (parity highlights) - `issued.create` / `validate` (query) / `touch` / `revoke` / `revokeAll` - `issued.refresh` — rotate with grace period - `issued.update` / `getKey` / `list` (paginated + `effectiveStatus`) - Hourly sweep crons for expired and idle keys - `cleanupKeys` / `cleanupEvents` internal jobs ## Vault API (parity highlights) - `vault.putPlaintext` — component-side envelope encryption with AAD - `vault.getResult` — `{ ok, value } | { ok: false, reason }` - `vault.update` — metadata/TTL without re-encrypt - `vault.list` — paginated with `effectiveState` - `auditEvents.listEvents` — paginated audit trail - `vaultRotate.rotate` / `isRotationComplete` — KEK rotation drain - `vaultCleanup.cleanupSecrets` — expired secret cleanup ## Example See [`example/`](./example/) for a minimal Convex + Vite dashboard. ## License MIT