DevForge

How to rotate API keys safely in CI/CD without leaking them

Hard-coded, never-rotated API keys are one of the most common ways secrets leak from a pipeline. This guide walks through generating, rotating, revoking, and auditing keys from the command line with APIAuth — keeping plaintext out of your repo, your logs, and your CI config.

Last updated: June 2026 · ~6 min read

Why rotation matters (and why it usually doesn't happen)

Most teams know they should rotate credentials, but rotation is manual, error-prone, and easy to skip. The failure modes are familiar:

APIAuth is a small CLI that addresses each of these with an encrypted local keystore (AES-256-GCM), expiry-aware key generation, one-command rotation and revocation, and export formats that hand secrets to CI without writing them into your config files.

Step 0 — Install APIAuth

APIAuth is published to the project's own package index (it is not on public PyPI yet), so point pip at the index — or install straight from source:

# From the project package index (recommended)
pip install --index-url https://coding-dev-tools.github.io/pypi-index/simple/ apiauth

# Or directly from source (always tracks the latest code)
pip install git+https://github.com/Coding-Dev-Tools/apiauth.git

Requires Python 3.10+ — pure Python, no compiled wheels.

Step 1 — Generate a key with an expiry

Expiry is your safety net: even if rotation slips, the key dies on schedule. Scope each key to a service so you can rotate one integration without touching the rest.

apiauth generate api-key --name "Deploy Token" --service "api-gateway" --expiry-days 90

The plaintext value is printed once, at creation time. After that, only a hash is kept — APIAuth never stores the plaintext, so it can't leak it back.

Step 2 — Rotate on a schedule

Rotation issues a fresh value and hashes out the old one, so the previous key stops verifying:

apiauth list                 # find the key id + expiry status
apiauth rotate <key-id>       # issue a new value, retire the old

Run this on a cadence (e.g. every 90 days, matching the expiry window). Pair it with your secret store's update step so the new value propagates to running services.

Step 3 — Revoke instantly if a key leaks

If a credential is exposed, you don't want to wait for rotation — kill it now:

apiauth revoke <key-id>

Revocation is immediate; apiauth verify will reject the key from that point on.

Step 4 — Hand keys to CI without writing them to config

The leak-prone move is pasting secrets into a workflow file or an unencrypted .env committed to the repo. Instead, export at runtime into the CI environment. APIAuth has a dedicated GitHub Actions format that writes to $GITHUB_ENV:

# In a GitHub Actions step
apiauth export --format github-actions --service "api-gateway" >> "$GITHUB_ENV"

# Other targets
apiauth export --format env      --service production   # export KEY=value
apiauth export --format dotenv                          # .env style, no export prefix
apiauth export --format json                            # programmatic consumption

Step 5 — Fail the build on expired keys

Make stale credentials a hard error instead of a surprise outage. Audit the keystore as a pipeline gate:

apiauth audit --exit-on-expired

A non-zero exit stops the deploy before a dead key reaches production. apiauth stats gives a quick keystore overview for dashboards or scheduled reports.

Honest note on tiers. The free CLI covers getting started: generate, verify, and env-format export, with up to 5 keys. The CI workflow above — the non-env export formats (including github-actions), audit, stats, JWT custom claims, and unlimited keys — is part of the Individual plan ($12/mo). We'd rather tell you that up front than have a command fail in your pipeline.

What you actually get

CapabilityFreeIndividual ($12/mo)
generate / verify / revoke / rotate
Encrypted keystore (AES-256-GCM)
Number of keys5Unlimited
Export formatsenv onlyenv / dotenv / github-actions / json
JWT with custom claims
audit & stats

Source: the APIAuth README pricing & per-tier feature table. A Suite plan ($49/mo) covers all 11 Revenue Holdings CLI tools; Team ($79/mo) adds a shared keystore. Annual billing saves ~20%.

Stop hand-rotating secrets in your pipeline.

APIAuth is MIT-licensed and works fully offline — no telemetry, no phone-home.

Get APIAuth on GitHub Also: OpenAPI mock server guide →

FAQ

Where does APIAuth store my keys?

In an encrypted keystore under ~/.apiauth/, using AES-256-GCM. The master key (~/.apiauth/master.key) never leaves your machine, keys are stored as hashes, and plaintext is displayed only once at creation.

Does rotating a key invalidate the old one?

Yes — apiauth rotate issues a new value and hashes out the previous one, so the old key stops verifying. Use apiauth revoke to kill a key immediately without issuing a replacement.

Can I use it in GitHub Actions / GitLab CI?

Yes. apiauth export --format github-actions writes to $GITHUB_ENV; env and dotenv formats work for any shell-based pipeline. (Non-env formats are an Individual-plan feature.)

Is it free?

The CLI is free for individuals and open source (generate/verify/env-export, 5 keys). Unlimited keys, all export formats, JWT claims, and audit/stats are on the Individual plan at $12/mo.

APIAuth is part of Revenue Holdings — a suite of developer CLI tools. See also Envault for syncing and rotating .env files across environments.