Security Posture
Document 01 of 5 · Last reviewed:
This document explains what keyrotate does with your secrets and what it explicitly does not do.
What keyrotate does
- Verifies a candidate key against the upstream provider before writing it anywhere. If the provider rejects the key, nothing is written and the rotation aborts.
- Writes the verified key to each configured destination (1Password, GitHub Actions secrets, Supabase, Netlify, Fly.io, local
.env) via that destination's official CLI or HTTPS API. - Appends a JSONL audit entry to
~/.config/keyrotate/audit.logrecording timestamp, provider, destinations attempted / succeeded / failed, verifier result, and operator (your local username). Never the key value itself. - Optionally triggers a post-rotate verification workflow (a GitHub Actions workflow you specify) so you can confirm the new key reaches production.
- Offers to open the provider's revocation page so you can revoke the old key as the final step.
What keyrotate never does
- No telemetry. The binary makes HTTP calls only to providers and destinations you have explicitly configured in
keyrotate.toml. No analytics, no error reporting, no "anonymous usage stats." - No phone-home auto-update. The binary does not check for new versions or contact any keyrotate-controlled server. Updates happen only when you run
brew upgrade/npm update/ etc. - No cloud component. There is no keyrotate-controlled backend. All state (config, audit log) lives on the machine where you run the CLI.
- Secrets never written to disk except where you direct. The CLI holds the new key value in memory only long enough to verify and dispatch it.
- Secrets never logged. The audit log records outcomes, never values. Standard output prints
****placeholders for hidden-input prompts.
Where secrets travel during a rotation
- From your terminal's hidden-input prompt into the running keyrotate process (in memory).
- To the provider's verification endpoint, over HTTPS, via a single authenticated request (e.g.
GET https://api.openai.com/v1/models). - To each destination's API or CLI: 1Password (via
opCLI with stdin pipe), GitHub (viagh secret setwith stdin pipe), Supabase, Netlify, Fly.io (via their CLIs),.envfiles (written with mode0600). - Out of memory when the process exits.
Some destination CLIs (Supabase, Fly.io) accept secrets as command-line arguments rather than stdin; that means the value is briefly visible to ps on the local machine while the destination CLI is running. This is a limitation of the upstream CLI, not keyrotate. Patches to use management APIs directly are welcome.
1Password integration
keyrotate uses the official op CLI, which authenticates via your system's biometrics (Touch ID on macOS, Windows Hello on Windows). keyrotate never sees your 1Password master password or session token; it shells out to op and pipes the secret over stdin.
Local file permissions
~/.config/keyrotate/audit.log— created with mode0600(user-readable only).- Generated
.envfiles — written with mode0600. keyrotate.toml— your config; permissions inherited from creator. Recommendchmod 0644(no secrets are stored here, but it lists secret names).
Threat model — what keyrotate is and isn't designed to defend against
Designed for: reducing the number of manual steps in a rotation, eliminating the "I forgot to update Supabase" failure mode, and verifying the new key actually works before the old one is revoked.
Not designed for: defending against a compromised local machine. If an attacker has root or code execution on the machine running keyrotate, they can read the audit log, read .env files, and intercept secrets as they pass through memory. keyrotate assumes a trusted local environment.