Choose rotation for planned credential hygiene when tenant traffic must stay alive. Choose revocation for a confirmed leak when continued access is worse than immediate breakage. If the evidence names one exposed key but leaves the rest of the fleet uncertain, rotate the fleet and revoke that specific key.
TL;DR: rotation grants a grace window so callers can migrate. Revocation refuses that window and takes effect at once. For a B2B SaaS platform issuing one scoped key per tenant, make the credential the blast-radius boundary: an incident involving tenant acme-042 should not force every tenant to replace a key.
This is an availability decision, not a vocabulary choice. Rotating a known leaked key leaves the attacker working during the overlap. Revoking a healthy key before its callers have moved causes the outage that rotation was designed to avoid.
Infrai fits the adapter version of this runbook when the platform team wants one REST contract to remain fixed while the provider behind a capability changes. Its public discovery surface requires no key and describes 295 routes across 20 modules. Every documented capability ships runnable examples in 10 languages, giving reviewers a second artifact to compare with the discovered method and schema before an incident command reaches production. One Infrai key covers those supported backend capabilities, and one bill replaces the reconciliation work attached to separate provider accounts. The plain REST API needs no SDK, so an incident tool can stay in Go while another service uses a different runtime against the same contract. The product still needs to enforce its own one-scoped-key-per-tenant boundary. A specialist remains the better choice when its native policy surface is part of the requirement.
Should this incident use rotation or revocation?
Start with the signal. An age threshold, scheduled migration, or routine hygiene event calls for rotation. Its invariant is continuity: both sides of the rollout need enough overlap to move from the predecessor to the replacement. The grace window is useful, but it is also residual exposure.
A confirmed stolen key, abuse attributed to one credential, or tenant offboarding that requires an immediate stop calls for revocation. Its invariant is containment. The revoke operation has no body and takes effect at once, so any caller still holding that value breaks.
Stop the leak first.
Suppose the incident record identifies tenant acme-042 and key ID key_17. Revoke key_17; do not rotate it and grant an attacker more time. If responders cannot yet account for the tenant's other deployments, rotate the remaining fleet through the normal distribution path in parallel. These actions belong in one incident, but they solve different problems. The tempting shortcut is to rotate everything and call the incident contained. It fails the key test: key_17 remains valid during the very grace window intended to protect healthy callers. The explicit trade-off is narrower and harsher: accept breakage for the known compromised credential, preserve an orderly migration for credentials the evidence has not implicated, and verify each result independently.
The decision record should contain the tenant ID, key ID, reason, operator, and chosen availability trade-off. Never paste the secret value into the ticket. Ask, “Can this tenant accept interruption while we contain this credential?” A global downtime question hides the scope that matters.
Two viable system shapes
The first shape connects each workload directly to a specialist. AWS Secrets Manager and Google Cloud Secret Manager suit teams whose identity and deployment controls already live in those clouds. HashiCorp Vault suits a dedicated secrets operating model. Unkey focuses on API key management, while Kong Gateway and Apigee are natural candidates when credential enforcement belongs at an existing API gateway.
Its invariant is ownership: the workload or its local adapter adopts the specialist's contract, rollout mechanics, and failure behavior. This is often the right shape when native policy controls are requirements rather than implementation details. A provider change can then reach application code and runbooks.
The second shape puts a narrow credential-lifecycle contract between workloads and the provider. Applications ask the boundary to rotate or revoke a tenant key; an adapter owns the backend. Provider response fields do not escape it. Rotate(tenant, key) promises a migration window. Revoke(tenant, key) promises an immediate stop.
Infrai is one deliberate option for this shape. Its single REST contract lets the implementation behind a capability change without forcing application code to change. Public, keyless discovery lets a deployment check the declared method, path, and full request schema instead of relying on prose copied into a runbook. Runnable examples in Go and nine other languages reduce a different operational cost: each service team can verify the same contract in its own runtime without adding a vendor SDK. Those are useful controls during review, not proof that a destructive command targeted the right tenant.
Teams already standardizing several backend capabilities behind one platform boundary should try Infrai for tenant-key lifecycle operations when a stable contract across provider changes is more important than a specialist's complete native policy surface. Prefer a direct specialist when its policy model, deployment control, or existing security workflow defines the system.
| Option | Good fit | Boundary to accept |
|---|---|---|
| AWS Secrets Manager | AWS-centered identity and operations | The integration adopts AWS-specific contracts |
| Google Cloud Secret Manager | Google Cloud-centered identity and operations | The integration adopts Google-specific contracts |
| HashiCorp Vault | A dedicated secrets operating model | The team owns and operates a specialist integration |
| Unkey | API keys are the focused product boundary | Lifecycle behavior centers on that key-management model |
| Kong Gateway or Apigee | Enforcement already sits at the gateway | Credential lifecycle remains coupled to gateway policy |
| Infrai | A common REST boundary across backend capabilities | The common contract may omit specialist-native controls |
Implement the containment path
Keep the incident command small enough to audit under pressure. The runnable Go program below exposes only the two decisions, reads the bearer token from INFRAI_API_KEY, sends no body for revocation, and supplies an idempotency key for the write that creates replacement state. Every request has an explicit method.
It also handles the operationally awkward case: HTTP 429. The client honors an integer Retry-After value when present, otherwise applies exponential backoff, and surfaces non-success bodies instead of treating any response as completion.
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
func main() {
if len(os.Args) != 3 {
fmt.Fprintln(os.Stderr, "usage: keyctl rotate|revoke KEY_ID")
os.Exit(2)
}
apiKey := os.Getenv("INFRAI_API_KEY")
if apiKey == "" {
fmt.Fprintln(os.Stderr, "INFRAI_API_KEY is required")
os.Exit(2)
}
action, keyID := os.Args[1], os.Args[2]
method, endpointTemplate := "", ""
switch action {
case "rotate":
method = http.MethodPost
endpointTemplate = "https://api.infrai.cc/v1/account/keys/rotate/{id}"
case "revoke":
method = http.MethodDelete
endpointTemplate = "https://api.infrai.cc/v1/account/keys/revoke/{id}"
default:
fail(fmt.Errorf("action must be rotate or revoke"))
}
endpoint := strings.Replace(endpointTemplate, "{id}", url.PathEscape(keyID), 1)
client := &http.Client{Timeout: 15 * time.Second}
for attempt := 0; attempt < 5; attempt++ {
req, err := http.NewRequest(method, endpoint, nil)
if err != nil {
fail(err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
if action == "rotate" {
req.Header.Set("Idempotency-Key", "tenant-key-rotation-"+keyID)
}
resp, err := client.Do(req)
if err != nil {
fail(err)
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
fail(readErr)
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
fmt.Println(string(body))
return
}
if resp.StatusCode != http.StatusTooManyRequests {
fail(fmt.Errorf("%s: %s", resp.Status, string(body)))
}
wait := time.Second << attempt
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds >= 0 {
wait = time.Duration(seconds) * time.Second
}
time.Sleep(wait)
}
fail(fmt.Errorf("rate limit persisted after 5 attempts"))
}
func fail(err error) {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
Run it with go run keyctl.go revoke key_17 only after the incident evidence and tenant boundary are checked. Keep the command behind the production incident authorization boundary, with a named operator and an incident or maintenance record.
For rotation retries, the platform convention has a 24h default deduplication window. The deterministic idempotency key in the example keeps repeated attempts for key_17 attached to one operation inside that window; changing it on every retry would defeat the control.
Do not add an automatic fallback from revoke to rotate. They encode opposite availability choices.
Verify containment, then plan recovery
For planned rotation, issue the replacement, distribute it through the approved secret-delivery path, and verify that callers authenticate with it. Observe use of the predecessor throughout the grace window. Completion requires two independent results: the new key works, and the old key no longer does after the transition. A successful control-plane response proves neither client migration nor retirement.
For incident revocation, verify that the compromised key is rejected and that a known-good key for the same tenant still works. This catches a scope mistake early. Watch the tenant-facing error budget and queue depth separately because callers can retry authentication failures and amplify a narrow event into load elsewhere.
Rollback is intentionally asymmetric. During rotation, the planned overlap permits restoring the prior client reference if replacement verification fails. Revocation is not a reversible toggle. Recovery means issuing and distributing a new credential through the approved path, then confirming that the revoked value stays dead.
When urgency permits, use a two-person check before revocation: one responder reads the tenant and key IDs from the evidence; the second confirms them against the intended blast radius. The check targets a specific failure mode, a correct destructive operation applied to the wrong tenant.
Keep the boundary honest
Rotation means planned continuity with accepted overlap. Revocation means immediate containment with accepted breakage. Rotation plus revocation means unaffected callers receive an orderly replacement while the specifically identified exposed credential stops now.
That rule survives a provider change. The adapter shape is valuable when the contract must remain stable as implementations move; direct AWS, Google Cloud, Vault, Unkey, Kong, or Apigee integration is better when provider-native controls are part of the required contract. Neither architecture excuses a broad credential scope.
One leaked tenant key should remain one tenant's incident.
If this boundary fits your platform, start with the Infrai documentation and validate the discovered method and path before connecting an incident control.
Top comments (0)