secretdef
like @types for env vars
agent-friendly
devops simplified
open

Your app has secret dependencies. Declare them.

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 secretdef

Quick start with AI

Paste 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. The object key IS the env var name. Include:
- description: what it is, expected format/prefix, a dashboard URL to provision it
- validate: use a built-in validator (str, bool, num, email, host, port, url, json) or a custom function
- devDefault: a safe default for local development if applicable
- example: a sample value to show in error output (e.g. "sk_live_...")

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

Deploys fail with useless errors

TypeError: Cannot read properties of undefined tells you nothing. You redeploy 3 times before you find the missing key.

Nobody knows what's provisioned

Is Stripe set up in staging? Which Supabase project is prod pointing at? Where do you get a Resend key?

AI agents hit a wall

Cursor and Claude Code scaffold integrations in seconds — then crash on a missing env var with a generic TypeError.

How it works

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.

1.List what your app needs

src/secrets.ts
// src/secrets.ts
import { defineSecrets } from 'secretdef';

export const secrets = defineSecrets({
  STRIPE_SECRET_KEY: {
    description: 'Stripe API secret key — https://dashboard.stripe.com/apikeys',
    example: 'sk_live_...',
    validate: 'str',
    devDefault: 'sk_test_placeholder',
  },
  DATABASE_URL: {
    description: 'Postgres connection string — format: postgresql://user:pass@host/db',
    validate: 'url',
    devDefault: 'postgresql://localhost:5432/myapp_dev',
  },
});

2.Validate at startup

src/env.ts
// 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,
});
Prefer less wiring? Use auto-register instead
src/index.ts
// Or use auto-register — less wiring, same result
import { enableAutoRegister, validateSecrets } from 'secretdef';
enableAutoRegister();

import './secrets';
import './modules/db/secrets';

const env = validateSecrets();

3.Get actionable errors instead of cryptic crashes

Terminal
🔴 Missing 2 secret(s) [env=production]:
✗ STRIPE_SECRET_KEY
Stripe API secret key. Starts with sk_live_ (not pk_).
→ https://dashboard.stripe.com/apikeys
from: src/secrets.ts

✗ DATABASE_URL
Postgres connection string — format: postgresql://user:pass@host/db
from: src/modules/db/secrets.ts
✓ STRIPE_WEBHOOK_SECRET — set (from: src/secrets.ts)

Doesn't block your dev server

secretdef adapts to the environment. You don't need every secret configured to start working locally.

Production

Missing required secrets print an error table and exit the process. No surprises in prod.

Development

Missing secrets print a warning. The server starts. They throw only when code actually reads the missing value.

At point of use

useSecret() throws a structured error with the var name, description, and dashboard URL.

Built for AI agents

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.

Actionable errors at the point of use

process.env.KEY returns undefined silently. useSecret('KEY') tells you what's wrong, where to find the value, and which file declared the requirement.

src/modules/stripe/client.ts
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 undefined

Errors that fix themselves

The 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_SECRET_KEY

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_SECRET_KEY

Clerk backend API key. Starts with sk_live_ or sk_test_. Dashboard: https://dashboard.clerk.com → API Keys. Not the publishable frontend key (pk_).

DATABASE_URL

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.

Every module declares its secrets

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
stripe
@secretdef/paypalsoon
@paypal/checkout-server-sdk
@secretdef/openaisoon
openai
@secretdef/anthropicsoon
@anthropic-ai/sdk
@secretdef/supabasesoon
@supabase/supabase-js
@secretdef/clerksoon
@clerk/nextjs
@secretdef/auth0soon
auth0
@secretdef/firebasesoon
firebase-admin
@secretdef/sendgridsoon
@sendgrid/mail
@secretdef/resendsoon
resend
@secretdef/postmarksoon
postmark
@secretdef/awssoon
@aws-sdk/client-s3
@secretdef/gcpsoon
@google-cloud/storage
@secretdef/planetscalesoon
@planetscale/database
@secretdef/neonsoon
@neondatabase/serverless
@secretdef/tursosoon
@libsql/client
@secretdef/twiliosoon
twilio
@secretdef/segmentsoon
analytics-node
@secretdef/mixpanelsoon
mixpanel

Added secret definitions to your SDK or service? Open a PR to add it to the supported list — help others discover it.

Add your service
$npm i secretdef

Zero-dependency. ~2KB. Works with any secrets provider.