Commands
apply
Rotate, propagate, verify, grace. Split into two phases so auto adapters run unattended and manual-assist adapters don't block CI.
The two phases
Default is --auto-only. Pass
--manual-only explicitly to run the interactive phase.
The two are mutually exclusive. Auto completes in minutes; manual
pauses per rotation and takes as long as you need.
--auto-only (default)
Runs every adapter with mode: "auto". Unattended, safe
in CI, safe in agent mode. Adapters with mode: "manual-assist"
are deferred. The CLI announces them at the end with a
rotate-cli apply --manual-only hint.
--manual-only
Runs adapters with mode: "manual-assist". Each rotation
pauses:
- CLI prints the dashboard URL and instructions.
- You create the new secret in the provider dashboard, paste it.
- CLI verifies, propagates to consumers, starts grace.
- After grace, CLI prompts for revoke, you delete the old secret in the dashboard.
Requires an interactive TTY. Agent mode refuses this flag.
Full flag list
| Flag | Meaning |
|---|---|
--provider <name> | Filter by adapter. |
--tag <name> | Filter by tag (non-sensitive, sensitive). |
--from-scan | Read the scan cache instead of rotate.config.yaml. |
--auto-only | Default. Run only mode: "auto" adapters. |
--manual-only | Run only mode: "manual-assist" adapters. |
--confirm-bulk | Required when --from-scan picks >20 rotations. |
--yes | Skip interactive confirmations. |
--reason <string> | Justification (required in agent mode). |
--max-rotations <n> | Hard cap (required in agent mode). |
--skip-unknown | Skip secrets where ownership couldn't be determined. |
--force-rotate-other | Rotate even when ownership says "other" (dangerous). |
--no-ownership-check | Disable the gate entirely (forbidden in agent mode). |
--parallel <n> | Consumer propagation concurrency (default 10). |
--no-verify | Skip verify step (forbidden in agent mode). |
--audit-log <path> | Append-only JSON audit of every action. |
How it works
- Ownership gate. Re-runs
whologic, skipsotherunless--force-rotate-other. - Dedup. Groups entries by
(adapter, hash(currentValue)). Same value across projects becomes one rotation with all copies updated. - Create. Adapter mints a new secret (auto mode) or prompts the user (manual mode).
- Propagate. Each registered consumer writes the new value (Vercel env, GitHub secret, local
.env). - Trigger. Fire redeploys or workflows if the consumer supports it.
- Verify. Hit the deployment's canonical endpoint with the new value.
- Grace. Rotation enters a 1h grace period with the old secret still valid. Use
rotate-cli statusto watch. - Revoke.
rotate-cli revoke <rotation-id>when consumers are synced. Old secret invalidated.
Example: bulk incident response
$ rotate-cli apply --from-scan --tag non-sensitive \ --yes --confirm-bulk \ --reason "vercel-apr-2026 breach" Fetching siblings... ✓ 14.3s Grouping 76 entries... 58 unique, 18 deferred to manual phase [1/58] ✓ clerk-elements rotated · 2.1s [2/58] ✓ vercel-ai-gateway-latex0 rotated · 0.8s ... [58/58] ✓ resend-railly rotated · 1.4s ✓ 58 rotations in grace (1h) ⚠ 18 rotation(s) deferred to the other phase: firecrawl (10), trigger-dev (5), uploadthing (3) → rotate-cli apply --from-scan --manual-only
JSON envelope
{ "command": "apply", "status": "success", "data": { "mode": "auto-only", "rotations": [{ "rotation_id": "rot_123", "status": "in_grace", ... }], "deferred": [{ "secret_id": "firecrawl-visionboard", "adapter": "firecrawl" }], "ownership_summary": { "self": 58, "other": 0, ... } }, "next_actions": [ "Run `rotate status rot_123 --json` to check sync; `rotate revoke rot_123` when ready", "18 rotation(s) deferred to the other phase: firecrawl (10), ..., run `rotate-cli apply --manual-only`" ] }