Skip to content

Onboarding an upstream credential

You have an API key, an OAuth refresh token, an AWS access key, or any other reusable upstream credential. You want it held only in Excalibur, and replaced everywhere else with a label.

There are two paths:

  • Operator-provisioned: you have the credential value in hand (vault, password manager, fresh from the provider's dashboard) and you provision it directly. See §1.
  • Adopt-from-traffic: a workload already used a raw credential while talking to an upstream through Excalibur, and you want to replace it with a managed placeholder without ever copy-pasting the secret. See §2.

Both paths end the same way: the placeholder appears on the Escrow & Surrogates page and is immediately usable.


1. Operator-provisioned

Use this path when you already have the credential value.

Pick a placeholder name

Convention: XCALIBUR_<UPPERCASE_WHAT_THE_WORKLOAD_CALLS_IT>. This is the literal string the workload will hold in its environment or config. It is intended to be visible — in source, in logs, in screenshots.

Examples:

XCALIBUR_STRIPE_SECRET_KEY
XCALIBUR_GITHUB_TOKEN
XCALIBUR_AWS_ACCESS_KEY_ID
XCALIBUR_AWS_SECRET_ACCESS_KEY
XCALIBUR_BANK_API_KEY

Provision it

excalibur-ctl placeholder add \
  --name      XCALIBUR_STRIPE_SECRET_KEY \
  --value     sk_live_… \
  --provider  stripe

Or by API:

curl -X POST http://localhost:8443/api/placeholders \
  -H "Content-Type: application/json" \
  -d '{
        "placeholder":  "XCALIBUR_STRIPE_SECRET_KEY",
        "real_value":   "sk_live_…",
        "provider_id":  "stripe",
        "route_family": "stripe"
      }'

route_family is what translation-authorisation rules will match against. For the built-in adapters (GitHub, Stripe, AWS, …) the route family is the same as the provider ID.

Verify

The placeholder appears immediately in the Placeholder vault table on the Escrow & Surrogates page:

Placeholder vault

Columns: Placeholder, Provider, Route family, Last rotated.

Use it

The workload references the placeholder by name. No SDK, no helper. The proxy substitutes the real value inline.

export STRIPE_SECRET_KEY=XCALIBUR_STRIPE_SECRET_KEY

curl https://api.stripe.com/v1/charges \
  -H "Authorization: Bearer $STRIPE_SECRET_KEY" \
  -d amount=1500 -d currency=eur

The upstream sees the real sk_live_…. The workload only ever sent the literal label.

What gets recorded

Event Details
placeholder_added placeholder, provider, route family
placeholder_updated on every value update (rotation)
placeholder_rotated on rotation specifically (also visible in Credentials > Credential lifecycle)

The lifecycle stream is visible at the bottom of the Escrow & Surrogates page.

Required prerequisite

Before the placeholder can be substituted, the calling principal must have a matching translation rule. A placeholder without a rule fails closed with translation_denied. See Translation rules.


2. Adopt a credential Excalibur has already seen

When a workload already running through Excalibur sends a real upstream credential (a raw API key in an Authorization header, a secret in a JSON body, an OAuth refresh token in a form field, …), Excalibur captures it as an onboarding candidate in the escrow store. The dashboard surfaces it on the Onboarding page so you can adopt it as a managed placeholder.

Info

The candidate API never returns the real secret value. You do not retype it. You promote the captured escrow entry directly into a placeholder.

Find the candidate

Open Onboarding:

Onboarding candidates

The Credential onboarding table lists every unmanaged credential seen in outbound traffic, with columns:

Column Meaning
Last seen Timestamp of the most recent observation
Provider Provider attribution (github, stripe, aws, …)
Artifact bearer / refresh-token / api-key / cookie / form-field / json-field
Where Header name, query param name, JSON key name, multipart field, …
Host Upstream host
Path Path prefix
Seen Number of observations
Fingerprint Stable, redacted identifier — never the value
Status unmanaged until adopted

Adopt it

In the Adopt candidate card below the table:

  1. Paste the candidate's escrow ID into the first field.
  2. Type the placeholder name you want this credential to live under (convention: XCALIBUR_<PROVIDER>_<WHAT_IT_IS>).
  3. Click Adopt.

The result panel shows the registered placeholder and the replacement snippet to use in the workload's config.

The candidate disappears from the list (its status flips to managed) and the new placeholder appears in Escrow & Surrogates > Placeholder vault.

Or via API

curl -X POST http://localhost:8443/api/onboarding/candidates/<escrow_id>/adopt \
  -H "Content-Type: application/json" \
  -d '{"placeholder":"XCALIBUR_STRIPE_SECRET_KEY"}'

Replace the raw secret in the workload

The result snippet tells you exactly what to change. Typically:

- STRIPE_SECRET_KEY=sk_live_51N4xQ2K9pRuC8mXz7YbVgF3hJ
+ STRIPE_SECRET_KEY=XCALIBUR_STRIPE_SECRET_KEY

Re-deploy. Future requests use the placeholder; Excalibur restores the same captured real value upstream.

What gets recorded

Event Details
onboarding_candidate_seen First time a fresh unmanaged credential is observed
placeholder_added When the candidate is adopted
onboarding_candidate_adopted Records the bound escrow ID and placeholder

What this path does NOT do

  • Excalibur does not scan local files, shell history, or process memory on the workload.
  • Excalibur does not return real secret values through the onboarding API or the dashboard.
  • Excalibur does not ask you to re-enter a secret it already has.

If you want to bring a credential under management that is not already in live traffic, use the operator-provisioned path in §1.


What's next