Every service you integrate needs API keys, tokens, and connection strings. secretdef is a standard way for modules to declare which secrets they need and where to get them.
npm i secretdefPaste this prompt into Claude Code, Cursor, Codex, or any AI coding tool. It'll scan your codebase and generate all the secret definitions for you.
Scan this codebase for all process.env usage. For each secret found, use defineSecrets() from "secretdef" to declare it — right in the file where it's used. Include: - envVar: the environment variable name - description: what it is, expected format/prefix, a dashboard URL to provision it - envOverrides: if dev/staging use different keys or have defaults Use useSecret() to access values — it throws structured errors with the var name, description, and dashboard URL, so missing secrets are obvious to both humans and AI agents. Wire up validateSecrets() at the app entry point to catch missing secrets at startup. Install: npm i secretdef Docs: https://secretdef.com/docs
TypeError: Cannot read properties of undefined tells you nothing. You redeploy 3 times before you find the missing key.
Is Stripe set up in staging? Which Supabase project is prod pointing at? Where do you get a Resend key?
Cursor and Claude Code scaffold integrations in seconds — then crash on a missing env var with a generic TypeError.
You list the secrets your app needs — the env var name, a description, and where to get the value. At startup, one call checks everything and tells you exactly what's missing.
// src/secrets.ts
import { defineSecrets } from 'secretdef';
export const secrets = defineSecrets({
STRIPE_SECRET_KEY: {
envVar: 'STRIPE_SECRET_KEY',
description: 'Stripe API secret key. Starts with sk_live_ (not pk_). ' +
'https://dashboard.stripe.com/apikeys',
envOverrides: {
development: { envVar: 'STRIPE_TEST_SECRET_KEY' },
},
},
DATABASE_URL: {
envVar: 'DATABASE_URL',
description: 'Postgres connection string — format: postgresql://user:pass@host/db',
},
});// src/env.ts — validate everything at startup
import { validateSecrets } from 'secretdef';
import { secrets as app } from './secrets';
import { secrets as db } from './modules/db/secrets';
export const env = validateSecrets({
...app,
...db,
});// Or use auto-register — less wiring, same result
import { enableAutoRegister, validateSecrets } from 'secretdef';
enableAutoRegister();
import './secrets';
import './modules/db/secrets';
const env = validateSecrets();secretdef adapts to the environment. You don't need every secret configured to start working locally.
Missing required secrets print an error table and exit the process. No surprises in prod.
Missing secrets print a warning. The server starts. They throw only when code actually reads the missing value.
useSecret() throws a structured error with the var name, description, and dashboard URL.
Agents read the structured errors to self-resolve missing secrets — env var name, dashboard URL, registering file, all in one message. And when generating integrations, they write rich defineSecrets descriptions with format hints, provisioning links, and common gotchas.
process.env.KEY returns undefined silently. useSecret('KEY') tells you what's wrong, where to find the value, and which file declared the requirement.
import { useSecret } from 'secretdef';
const key = useSecret('STRIPE_SECRET_KEY');
//
// If missing → throws with:
// SecretNotAvailable: STRIPE_SECRET_KEY is not configured.
// Environment variable: STRIPE_SECRET_KEY
// Description: Stripe API secret key
// Where to find it: https://dashboard.stripe.com/apikeys
// To fix: set STRIPE_SECRET_KEY in your .env file.
//
// vs: TypeError: Cannot read properties of undefinedThe descriptions you write show up verbatim in error output. A dashboard link and a format hint turn a debugging session into a copy-paste fix — or let Claude Code figure it out on its own.
Stripe API secret key. Starts with sk_live_ (prod) or sk_test_ (dev). Not the publishable key (pk_). Provision at https://dashboard.stripe.com/apikeys — requires Admin role.
Clerk backend API key. Starts with sk_live_ or sk_test_. Dashboard: https://dashboard.clerk.com → API Keys. Not the publishable frontend key (pk_).
Postgres connection string, format: postgresql://user:pass@host:5432/db. Neon: https://console.neon.tech → Connection Details. Supabase: Settings → Database → URI. Use the pooler URL for serverless.
Your database module knows it needs DATABASE_URL. Your payment code knows it needs a Stripe key. When each module ships a secrets.ts, the whole app's requirements become discoverable, validated, and documented — automatically.
For services that don't ship their own definitions yet, the community maintains ready-made @secretdef/* packages:
@secretdef/stripesoon@secretdef/paypalsoon@secretdef/openaisoon@secretdef/anthropicsoon@secretdef/supabasesoon@secretdef/clerksoon@secretdef/auth0soon@secretdef/firebasesoon@secretdef/sendgridsoon@secretdef/resendsoon@secretdef/postmarksoon@secretdef/awssoon@secretdef/gcpsoon@secretdef/planetscalesoon@secretdef/neonsoon@secretdef/tursosoon@secretdef/twiliosoon@secretdef/segmentsoon@secretdef/mixpanelsoonAdded secret definitions to your SDK or service? Open a PR to add it to the supported list — help others discover it.
Add your servicenpm i secretdefZero-dependency. ~2KB. Works with any secrets provider.