Provider keys
ProA settings card for bring-your-own-key AI products. Each provider row shows its real mark, the masked key, a status and when the key was last used. You can reveal a key for five seconds, copy, rotate or remove it with undo. Adding or rotating a key opens an inline form that checks the format as you type, then tests the connection before saving.
Provider keys
Your own keys, encrypted at rest and only used by this workspace.
- Anthropicsk-ant-••••4f2a
- OpenAIsk-proj-••••9c1e
- Google AIAIza••••Qk7w
- Mistral••••h7Qd
- xAIGet one at console.x.aiNot set
- A settings page where users add their own Anthropic, OpenAI, Google AI, Mistral or xAI keys.
- Showing which providers are connected, which keys were rejected, and when each key was last used.
- Letting users rotate a leaked key or remove one with a short undo window.
- Choosing a model for a request; use model-picker.
- Managing your own product's API keys for customers with scopes and expiry; use secret-rotation or a table.
- Showing spend or rate limits; use usage-meter.
Installation
Pro source and install commands unlock with a Pro plan.
import { ProviderKeys } from "@/components/arc/provider-keys/provider-keys"; // Sample workspace, every test simulated<ProviderKeys /> // Wired to your API<ProviderKeys keys={keys} onKeysChange={setKeys} testKey={({ provider, key, signal }) => fetch("/api/provider-keys/test", { method: "POST", body: JSON.stringify({ provider, key }), signal }).then(r => r.json())} onSave={(provider, key) => saveKey(provider, key)} onRemove={provider => deleteKey(provider)}/>ProviderKeys
A settings card for bring your own key AI products. One row per provider (Anthropic, OpenAI, Google AI, Mistral, xAI) with its real mark, the masked key (sk-ant-••••4f2a), a status as icon plus label (Connected, Invalid, Not set), when the key was last used, and four actions: reveal for five seconds with a countdown ring, copy, rotate and remove with undo. Clicking a status retests the stored key. Add key and Rotate open an inline form under the row that checks the key's format as you type, then shows Testing connection and Connected before the row updates. With no props it renders a sample workspace and simulates every test.
keysProviderKey[]–Controlled keys: { provider, status: "connected" | "invalid" | "unset", value?, last4?, lastUsedAt?, checkedAt?, error? }. Pass value to enable reveal and copy, or only last4 when your server never returns full keys. Providers without an entry read Not set.defaultKeysProviderKey[]sample workspaceStarting keys when uncontrolled. Pass [] for the empty state, every row Not set with an Add key button.onKeysChange(keys: ProviderKey[]) => void–Every change: added, rotated, removed, restored with undo, or retested.providersProviderId[]["anthropic", "openai", "google", "mistral", "xai"]Which providers get a row, in order.testKey(request: { provider, key?, signal }) => Promise<KeyTestResult>–Tests a key and resolves { ok: true, detail? } or { ok: false, error }. key is the pasted key when adding or rotating, and missing when retesting a stored key (test it on your server by provider). Aborted when the form closes. Leave it out to simulate: stored invalid keys fail again, everything else connects in under a second.onSave(provider, key, mode: "add" | "rotate") => void–A new key passed its test. Persist it here; the row already shows it as connected.onRemove(provider) => void–Fires when the undo window closes, so a removal only reaches your server once it sticks.onCopy(provider) => void–A key was copied to the clipboard, for an audit log.revealDurationnumber5000Milliseconds a revealed key stays visible before it masks itself again.undoDurationnumber5000Milliseconds Undo stays after a removal.loadingbooleanfalsePlaceholder rows with the real row geometry while keys load.titleReactNode"Provider keys"Card title.descriptionReactNode–Line under the title. Falsy hides it.footerReactNode | null–Quiet line under the list. Defaults to an encryption note; null hides it.classNamestring–Extra class on the card.- Tab
- Moves through each row's status (retest), reveal, copy, rotate and remove, or Add key on an empty row.
- Enter
- In the form, saves the key and runs the connection test.
- Escape
- In the form, cancels and returns focus to the button that opened it.
- A section labelled by its title, with the rows in a list. Every icon button has a provider-specific label, such as "Copy the Anthropic key".
- Status is never color alone: an icon plus Connected, Invalid, Not set or Testing. The status button's label includes the rejection reason.
- Reveal is a toggle with aria-pressed; rotate and Add key report aria-expanded for the inline form.
- A polite live region announces reveals, copies, test results, removals and undo. Without testKey it adds "Simulated, nothing was sent."
- The form's input has a label and points at the helper line with aria-describedby; format and rejection errors set aria-invalid.
- Focus is managed: the form focuses its input, Cancel returns focus to its trigger, Remove moves focus to Undo, and Undo returns it to Remove.
- Status, key, meta and helper text swap with one crossfade (out 100ms, in 160ms with a 2px rise) in a shared grid cell, so nothing resizes.
- The inline form opens and closes on a measured height spring (visualDuration .24, no bounce) with a short fade.
- The reveal countdown is a hairline ring that empties linearly over revealDuration. Icon swaps (eye, copy check) scale and fade in 160ms.
- Reduced motion keeps every state and announcement, drops the countdown ring, the rise and the press scale, and slows the spinner.
- At 560px and wider each row is one line of columns: mark, name over key, status over last used, actions.
- Below 560px the status moves beside the name, the key gets its own line, and last used shares a line with the actions.
- The form puts input, Cancel and Save on one line on wide cards and stacks them under 560px. Holds at 360px with no horizontal scroll.
- Plain DOM and CSS; Motion only animates the form height and short crossfades. The clock re-renders the card every 30 seconds for relative times.
- Timers and in-flight tests are cleared and aborted on unmount and when a form closes.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use for bring your own key settings in AI products: a workspace or account settings page, an onboarding step, or a gateway admin.
- Never ship full keys to the browser in production: pass last4 only and leave value out, which disables reveal and copy for that row.
- testKey should call the provider from your server (for example a models list request) and return { ok: false, error: "401 Unauthorized" } on rejection.
- Rotate means paste a new key: the row keeps the old key until the new one passes its test, then swaps.
- For a single generic secret with expiry and rotation history use secret-rotation; for spend per provider pair it with usage-meter.
The full library index for assistants is at /llms.txt.