The Secrets Management Gap
You built an app with Cursor, deployed it, and a security researcher found your Stripe secret key in your GitHub repository. Your key was committed, pushed to GitHub, and is now publicly accessible. You have to rotate the key, update all services that use it, and deal with the security incident. What happened? Your AI assistant hardcoded the secret directly in the source code, and you committed it without noticing. This is the secrets management gap, and it is one of the most dangerous security failures in AI-generated apps. AI assistants hardcode secrets because they do not understand the difference between code (which is committed to version control) and configuration (which is not). Here are the 6 reasons AI assistants hardcode secrets, and the production checklist to fix them.
The direct answer is that secrets management is the practice of storing secrets (API keys, passwords, tokens) securely, outside of the source code, and injecting them at runtime via environment variables or a secrets manager. AI assistants hardcode secrets because the LLM's training data includes examples with hardcoded values, and the LLM does not understand that secrets should not be committed to version control. The 6 reasons are: hardcoded API keys, hardcoded database passwords, hardcoded JWT secrets, hardcoded OAuth credentials, no environment variable usage, and no secrets scanning. Each one has a known cause and a known fix. For more on security, see our article on the security headers gap.
Reason 1: Hardcoded API Keys
The most common reason AI assistants hardcode secrets is hardcoded API keys. AI assistants write code like const stripe = require('stripe')('sk_live_abc123') directly in the source file, which means the API key is committed to version control. The fix is to use environment variables: const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY), and to set the STRIPE_SECRET_KEY environment variable in the Deployxa dashboard. Never hardcode API keys in source code. For more on environment variables, see our article on the vibe coder's guide to environment variables.
Reason 2: Hardcoded Database Passwords
The second reason is hardcoded database passwords. AI assistants write connection strings like mongodb://user:mypassword@host:27017/db directly in the source code, which means the password is committed to version control. The fix is to use environment variables: process.env.DATABASE_URL, and to set the DATABASE_URL environment variable in the Deployxa dashboard. For more on database configuration, see our article on fixing DATABASE_URL not set.
Reason 3: Hardcoded JWT Secrets
The third reason is hardcoded JWT secrets. AI assistants write jwt.sign(payload, 'mysecret') with a weak, hardcoded secret, which means the secret is committed to version control and is easily guessable. The fix is to use a strong, randomly generated secret stored as an environment variable: jwt.sign(payload, process.env.JWT_SECRET), and to generate the secret with openssl rand -base64 32. For more on JWT security, see our article on the JWT authentication trap.
Reason 4: Hardcoded OAuth Credentials
The fourth reason is hardcoded OAuth credentials. AI assistants write OAuth client IDs and client secrets directly in the source code, which means the credentials are committed to version control. The fix is to use environment variables: process.env.OAUTH_CLIENT_ID and process.env.OAUTH_CLIENT_SECRET, and to set them in the Deployxa dashboard.
Reason 5: No Environment Variable Usage
The fifth reason is no environment variable usage at all. AI assistants sometimes do not use environment variables for any configuration, which means all configuration (including secrets) is hardcoded. The fix is to use environment variables for all configuration that varies between environments (development, staging, production), especially secrets.
Reason 6: No Secrets Scanning
The sixth reason is no secrets scanning. Even if you use environment variables correctly, a developer might accidentally commit a secret (e.g., a temporary debug key, a test token). Without secrets scanning, the secret stays in the repository until someone notices it. The fix is to use a secrets scanning tool (e.g., GitGuardian, TruffleHog, GitHub Secret Scanning) that automatically scans commits for secrets and alerts on findings.
Step-by-Step: The 6-Fix Secrets Management Checklist
Fix 1: Replace hardcoded API keys with environment variables
// Bad
const stripe = require('stripe')('sk_live_abc123');
// Good
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);Fix 2: Replace hardcoded database passwords with environment variables
// Bad
const db = mongoose.connect('mongodb://user:mypassword@host:27017/db');
// Good
const db = mongoose.connect(process.env.DATABASE_URL);Fix 3: Replace hardcoded JWT secrets with environment variables
// Bad
const token = jwt.sign(payload, 'mysecret');
// Good
const token = jwt.sign(payload, process.env.JWT_SECRET);Fix 4: Replace hardcoded OAuth credentials with environment variables
// Bad
const oauth2Client = new google.auth.OAuth2('client_id', 'client_secret', 'redirect_uri');
// Good
const oauth2Client = new google.auth.OAuth2(
process.env.OAUTH_CLIENT_ID,
process.env.OAUTH_CLIENT_SECRET,
process.env.OAUTH_REDIRECT_URI
);Fix 5: Use a .env file for local development
Create a .env file for local development (never commit it):
# .env (never commit this file!)
STRIPE_SECRET_KEY=sk_test_abc123
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
JWT_SECRET=your-generated-secret
OAUTH_CLIENT_ID=your-client-id
OAUTH_CLIENT_SECRET=your-client-secretAdd .env to .gitignore:
echo ".env" >> .gitignoreCreate a .env.example file (commit this) to document required variables:
# .env.example (commit this)
STRIPE_SECRET_KEY=sk_test_your_key_here
DATABASE_URL=postgresql://user:pass@host:5432/db
JWT_SECRET=generate_with_openssl_rand_base64_32
OAUTH_CLIENT_ID=your_client_id
OAUTH_CLIENT_SECRET=your_client_secretFix 6: Enable secrets scanning
Enable GitHub Secret Scanning (free for public repos, available on private repos with GitHub Advanced Security). Additionally, install GitGuardian or TruffleHog for continuous scanning.
Step 7: Set environment variables in the Deployxa dashboard
In the Deployxa dashboard, add all environment variables with their production values. The pre-flight scanner will warn you about any that are clearly required but missing. For more on the pre-flight scanner, see our article on fixing DATABASE_URL not set.
Step 8: Verify with deployxa doctor
Run deployxa doctor to verify your app's health. The 14-point readiness engine checks SSL, DNS, environment variables, health endpoints, and container status.
Common Pitfalls and Troubleshooting
The first pitfall is committing .env to Git. This is a security incident, because your secrets are now in version control. The fix is to add .env to .gitignore immediately, remove it from your repository history (using git filter-branch or BFG Repo-Cleaner), and rotate all exposed secrets. The second pitfall is using NEXT_PUBLIC_ for secrets. Variables prefixed with NEXT_PUBLIC_ are inlined into the client-side JavaScript, which means anyone can see them. Never use NEXT_PUBLIC_ for secrets. The third pitfall is logging secrets. If you log environment variables (e.g., console.log(process.env)), the secrets appear in the logs, which creates a security risk. The fix is to never log environment variables, and to use a logger that supports redaction. For more on logging, see our article on the logging gap. The fourth pitfall is not rotating secrets. Secrets that are not rotated are vulnerable to compromise, because a stolen secret is valid forever. The fix is to rotate secrets regularly (e.g., every 90 days). For more on secret rotation, see our article on building an AI agent that manages your secrets. The fifth pitfall is not using a secrets manager for large teams. For teams with many secrets, a secrets manager (e.g., Doppler, AWS Secrets Manager, HashiCorp Vault) provides better security and management than plain environment variables.
Conclusion: Never Hardcode Secrets
The secrets management gap is not a sign that your AI assistant did a bad job. It is a sign that AI assistants do not understand the difference between code and configuration, and secrets management requires additional work. By applying the 6 fixes above (replace hardcoded API keys, database passwords, JWT secrets, OAuth credentials, use environment variables, enable secrets scanning), you can protect your app from secret leakage. Stop hardcoding secrets and start managing them securely.
Ready to ship a secure app? Drag your project to Deployxa Drop for an instant live preview, or install the CLI with npm i -g @deployxa/cli and deploy from your terminal. For more on AI coding patterns, see our articles on the security headers gap and the JWT authentication trap. Learn about the webhook reliability gap and the CDN configuration gap in our companion articles. Explore our free developer tools to speed up your workflow.