Secrets management
Keeping API keys and passwords out of your code, stored somewhere encrypted and handed to the app at runtime instead.
See it
What it is
The rule underneath it: a secret has exactly one home, and everything else holds a reference to it rather than a copy. The home is a manager (1Password, Doppler, Infisical, AWS Secrets Manager, HashiCorp Vault) or your host's encrypted environment store, and the app reads it at boot. Your repo carries a .env.example with every key name and no values, never a real .env.
Any key that costs money, sends email, or touches customer data belongs there from the first commit, because retrofitting means rotating everything you already leaked. The parts that matter in practice: one set of values per environment (dev keys must not reach prod), a rotation you have actually rehearsed once, and some record of who read what.
Two traps. Environment variables are not a secret manager. A managed platform may well encrypt the stored config, but once the process boots those values sit in plaintext in its environment, readable by anything with the same reach: crash reports and stack traces, 'docker inspect' and /proc, CI logs, an error handler that dumps process.env, and any dependency in your tree that decides to read it. A manager gives you scoped access, rotation, and an audit trail on top of storage, which is the part env vars never had. And the browser has no secrets at all, so anything bundled into client JS, including every NEXT_PUBLIC_ or VITE_ variable, ships to every visitor. If a third-party API needs a real key, proxy the call through your own server.
Ask AI for it
Set up proper secrets management in this project. First identify the secret store this project already has available (the host's encrypted config store, or a manager like 1Password, Doppler, Infisical, AWS Secrets Manager, or Vault) and make that the single source of the real values. Move every hardcoded key, token, password, and connection string out of the source, and inject the values at runtime through a scoped identity for that environment rather than a copied file. Add a typed config module that reads and validates all of them with zod at startup, so a missing secret fails fast on boot instead of at 3am. Commit a .env.example listing every key name with empty values, and ignore real values with '.env', '.env.*', and '!.env.example' so the example survives the ignore rule. Keep dev, staging, and prod values separate, keep every real secret server-side (nothing behind a NEXT_PUBLIC_ or VITE_ prefix), never print a secret value in output or logs, and finish with a list of which keys I must now rotate because they were previously committed.