# Managing secrets and API keys safely

URL: https://brainpowergroup.eu/en/blog/managing-secrets-and-api-keys-safely
Date: 2026-07-08 (updated 2026-09-23)
By: Boris, Brainpower Group

Secrets belong in a vault or your CI's secret store, never in git. Locally you use development keys with limited rights, and every key must be replaceable.

A secret should live in one place: a secrets manager, or your CI's secret store. Your application fetches it from there at deploy time or on startup. Your laptop holds only development keys with limited permissions, in a `.env` file that never reaches git, and every key should be replaceable without anything going down.

## What belongs in .env

A `.env` file is fine for local development. Add it to `.gitignore` from the very first commit and commit a `.env.example` with every variable name and dummy values. A new developer can then see straight away which variables they need.

Locally, use only keys with limited rights, such as the test mode of your payment provider or your own development database. Production secrets don't belong on a laptop, the lead's laptop included.

Secrets sometimes end up in places nobody notices. Docker's own documentation says build arguments and environment variables are inappropriate for passing secrets to a build, because they persist in the final image. Use `RUN --mount=type=secret` instead. Also never log your full configuration at startup, not even in debug mode.

Our developers work on managed, encrypted laptops with centrally managed accounts and SSO with two-factor authentication, as described in [how we work](/en/how-we-work). That protects the laptop, but not against a key that ends up in a repository.

## A vault for everything beyond your laptop

For staging and production, use a secrets manager. That can be your cloud's own, such as AWS Secrets Manager, Azure Key Vault or Google Secret Manager, or a separate tool like HashiCorp Vault. Which one you choose matters less than having only one.

Set up access per environment. A developer can read staging secrets, while production secrets only flow through the deployment pipeline. The vault's audit log shows who requested what.

In CI, you ideally want no long-lived cloud keys at all. GitHub Actions can use OpenID Connect to get short-lived tokens directly from your cloud provider, including AWS, Azure and Google Cloud. There is then no cloud key sitting in your repository settings waiting to leak.

## Plan rotation before you need it

For every secret, record who owns it and how to replace it. Do it now, because in the middle of an incident you don't want to be figuring out which services share the same database password.

Rotate at least when someone leaves the team, when a laptop goes missing, and when a key has been somewhere it shouldn't be. For keys with broad permissions, also agree on a fixed schedule.

Build your application so that two keys can be valid for a while, the old one and the new one. You add the new key, deploy, then revoke the old one, with no downtime. Short-lived tokens are better still: a token that expires after an hour never needs rotating.

Offboarding also means revoking tokens a person created themselves, such as personal access tokens for git or the cloud CLI. More on access and offboarding is in [security and GDPR with remote developers](/en/blog/security-and-gdpr-with-remote-developers).

## When a secret ends up in git anyway

Assume a pushed key has leaked, private repository or not. There are clones, forks, CI caches and backups you can't see.

So the first step is to revoke or rotate the key. GitHub's guidance says the same: revoke or rotate the secret first, after which rewriting history may not even be necessary. Then check the provider's logs to see whether the key was used.

If you still want to remove the secret from history, GitHub recommends git-filter-repo. It rewrites commits, so everyone on the team has to re-clone or rebase afterwards. Agree on that before you force-push.

Prevention takes less effort. Run a secret scanner such as gitleaks as a pre-commit hook, so a key never leaves your machine, and turn on push protection on GitHub. For your personal account it is enabled by default for pushes to public repositories. At repository level an administrator has to enable it, and that requires GitHub Secret Protection.

## AI coding tools and secrets

An AI agent such as Claude Code reads files in your working directory and runs commands. Whatever it reads goes to the model as context. A `.env` with real keys in your project is therefore a risk, even if you trust the tool.

We use Claude Code every day and have two agreements for it.

First, we put deny rules in the shared project settings, such as `Read(./.env)` and `Read(./.env.*)` in `.claude/settings.json`, so they apply to everyone on the team. According to the documentation, these rules block the built-in file tools and the commands Claude Code recognises, such as `cat`. A script that opens a file by itself is not covered. For a hard boundary at OS level, the documentation points to the sandbox.

Second, we keep production secrets out of the working directory. A file that isn't there can't be read by any tool, including an extension or script you forgot about.

Never paste a key into a prompt or a ticket, and look for hardcoded keys during code review, including in code an agent wrote. More team agreements for these tools are in [AI coding tools in a remote team](/en/blog/ai-coding-tools-in-a-remote-team).

## A checklist for your repository

- `.env` is in `.gitignore` and there is a `.env.example`.
- A secret scanner runs as a pre-commit hook and in CI.
- Push protection is on.
- Production secrets live only in the vault or your CI's secret store.
- CI gets cloud access through OIDC instead of a static key.
- Every secret has a known owner and a known rotation procedure.
- Deny rules for AI tools are in the shared project settings.

## Sources

- [Docker: build secrets](https://docs.docker.com/build/building/secrets/)
- [GitHub: OpenID Connect in GitHub Actions](https://docs.github.com/en/actions/concepts/security/openid-connect)
- [GitHub: removing sensitive data from a repository](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository)
- [GitHub: about push protection](https://docs.github.com/en/code-security/secret-scanning/introduction/about-push-protection)
- [gitleaks](https://github.com/gitleaks/gitleaks)
- [Claude Code: configure permissions](https://code.claude.com/docs/en/permissions)

## Frequently asked questions

**Should a .env file ever be committed to git?**

No. Add .env to .gitignore from the first commit and commit a .env.example with every variable name and dummy values. Real secrets for staging and production belong in a secrets manager.

**What should you do when an API key has been pushed to git?**

Revoke or rotate the key immediately and check the provider's logs to see whether it was used. Only then decide whether to rewrite history, for example with git-filter-repo.

**How do you stop an AI coding tool from reading your secrets?**

Keep production secrets out of your working directory and add deny rules for .env files to the tool's shared settings. In Claude Code, a rule such as Read(./.env) blocks the built-in file tools, but not a script that opens files on its own.

---

Source: https://brainpowergroup.eu/en/blog/managing-secrets-and-api-keys-safely
Brainpower Group, contact: https://brainpowergroup.eu/en/contact, hello@brainpowergroup.eu
