Key Rotate

Keeps old and new API keys valid during a rotation and shows which clients have switched, so cutting off the old key doesn't take production down.

StopsHelps prevent a key rotation taking production down

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/key-rotate.json

The files land in your project. They're yours to edit.

Usage

key-rotate-usage.tsx
import { KeyRotate, type KeyClient } from "@/components/ui/key-rotate"

<KeyRotate
  key={run}
  keyName="production secret key"
  clients={clients}
  defaultValue={0}
  rotatedAt={rotatedAt}
  onRotate={(overlap) => wait(() => (overlap ? setRotatedAt(Date.now()) : setDone("Old key expired. Clients still using it now get 401 errors until they're updated.")))}
  onRevokeOld={() => wait(() => setDone("Old key revoked."))}
/>

What it catches

  • Clients still calling with the old key
  • Rare jobs whose next call lands after the old key expires
  • Immediate revoke while the old key is still in use
  • Leaked key: allows an immediate cut-off on purpose

Props

KeyRotate props
PropTypeDefault
keyName* (required)

e.g. "Production secret key".

string—
clients* (required)

Who calls with this key and when they last did.

KeyClient[]—
value

Overlap window in ms (0 = the old key stops working immediately). Controlled.

number—
defaultValue

Initial overlap window in ms when uncontrolled.

number86_400_000
onValueChange

Called with the chosen overlap window in ms.

(overlap: number) => void—
options

Windows to offer, in ms.

number[][0, 3_600_000, 86_400_000, 604_800_000]
rotatedAt

When the new key was issued. Set it once rotation has started to show the overlap phase.

When—
quietFor

The old key must go unused this long (ms) before it can be cut off immediately.

number3_600_000
onRotate* (required)

Starts the rotation with the chosen window. A returned promise shows a pending state; a rejection is caught.

(overlap: number) => void | Promise<void>—
onRevokeOld

Revokes the old key during the overlap.

() => void | Promise<void>—
locale

BCP 47 locale for dates and times. Defaults to the viewer's.

string—
messages

Every visible string (see `KEY_ROTATE_MESSAGES`). The single-string props below still work and win.

Partial<KeyRotateMessages>—
optionLabel(overlap: number) => string—
rotateLabel(overlap: number) => string—
revokeLabelstring—
leakedLabelstring—
extendLabel(overlap: number) => string—

* required. Also accepts the native props of <section> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Panel labelled by its title; overlap options are native radio inputs with labels
  • Client status list and warnings are polite live regions with text, not only icons
  • A blocked revoke is announced with role=alert

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Enforce key expiry and revocation in the key service, and log last-used times per key so the client list is real.

  • Only knows the clients and last-seen times you pass
  • Rhythm-based predictions assume jobs keep their schedule
  • The overlap window is enforced by your key service, not this UI

What it stops

Helps prevent a key rotation taking production down

Also known as

api key · rotate key · secret rotation · credentials · revoke key · access token · env vars · webhook secret

Field type

Dependencies

Pairs well with