Rotating a credential without restarting the workload¶
This is the workflow Excalibur is most often hired for. The workload never sees the old key, never sees the new key, and never restarts.
The setup¶
The workload makes continuous requests using a placeholder:
while :; do
curl -sS -o /dev/null -w "%{http_code}\n" --max-time 4 \
https://api.stripe.com/v1/charges \
-H "Authorization: Bearer XCALIBUR_STRIPE_SECRET_KEY" \
-d amount=$RANDOM -d currency=eur
done
The proxy is substituting the current real value into every
outbound request. So far, so normal — 200 200 200 ….
Rotate¶
One operator command. Same placeholder name, new real value:
curl -X POST http://localhost:8443/api/placeholders \
-H "Content-Type: application/json" \
-d '{
"placeholder": "XCALIBUR_STRIPE_SECRET_KEY",
"real_value": "sk_live_NEW_VALUE_FROM_VAULT",
"provider_id": "stripe",
"route_family": "stripe"
}'
Or via CLI:
excalibur-ctl placeholder add \
--name XCALIBUR_STRIPE_SECRET_KEY \
--value sk_live_NEW_VALUE_FROM_VAULT \
--provider stripe
The POST /api/placeholders call is upsert — same name, new
value. The escrow record is replaced atomically.
What the workload sees¶
Nothing. The output keeps printing 200 200 200 ….
In the demo capture (scene 09 of the recorded walk-through), the
workload runs ~80 charges across the cutover. Every single one
returns 200. There are no 4xx, no 5xx, no retries.
in-flight responses across cutover:
HTTP 200 count 79
Why there are no failures¶
- Requests already in flight at the moment of the write complete against whichever value was active when the proxy looked the placeholder up. They are not rolled back, not re-signed, not cancelled.
- Requests that arrive after the write resolve to the new value on lookup. There is no warm-up period.
- The workload's HTTP client never retries because it never sees a failure. There is no race window for the workload to observe.
The only place the cutover is visible is the proxy itself — the new value appears in escrow, and the next outbound substitution uses it.
Where you see it in the dashboard¶
Open Escrow & Surrogates. The Placeholder vault table now shows a new "last rotated" timestamp on the row.
The Credential lifecycle panel at the bottom of the same page
streams a placeholder_rotated event in near-real-time.
What gets recorded¶
| Event | Details |
|---|---|
placeholder_updated |
Both the upsert path and the rotation path emit this |
placeholder_rotated |
Specifically signals a value change (not a metadata-only update) |
Both events show up on Audit as you would expect; filter by
type=placeholder_* to scope to credential lifecycle.
Common variations¶
Rotation integrates with vaults, webhooks, and translation-rule changes.
Scheduled rotation from a vault¶
Wire your existing vault rotation hook to call the same
POST /api/placeholders endpoint. Excalibur does not need to
participate in your secret-generation workflow — it is the read
side of the rotation, the workload-facing surface.
Webhook-driven rotation¶
Excalibur can also push notifications outbound on rotation. See
the webhook surface (POST /api/webhooks with
event_types=["credential_expired"] or
["credential_rotated"]) so downstream automation (audit log,
SIEM, ticketing) is informed every time a key turns over.
Rotation with translation-rule changes¶
If the rotated value must also have a different scope (e.g. a key that was full-access becomes read-only), update the relevant translation rule in the same operator window. The next request will be subject to the new rule immediately.
What this workflow does NOT require¶
- No workload restart, no rolling deployment.
- No coordination window with the workload team.
- No refresh API the workload has to call.
- No client SDK retries.
- No client-side awareness that anything happened.
That is the point.
