Skip to main content

Tutorial: add a client without redeploying

Goal: onboard a brand-new caller to a protected route by publishing trust material — without rebuilding or redeploying the adapter — and with no hosted service.

This is the property that makes TrustPlane Auth operationally cheap: trust is data, not code.

The idea

The adapter image, Deployment, and Service all stay exactly the same. Only the bundle changes.

Step 1 — The new client generates a key

trustplane gen-key
# keep the private key on the client; share only the PUBLIC key (base64url)

Step 2 — Merge the new client into existing bundles

trustplane bundle merge-source appends the new public key to your trust material and a matching source rule to the route — preserving everything already there:

trustplane bundle merge-source \
--trust-material trust-material.json \
--policy-bundle trustplane.bundle.json \
--out-trust-material trust-material.merged.json \
--out-policy-bundle trustplane.bundle.merged.json \
--issuer https://issuer.acme.demo/external-jwks \
--trust-domain acme.demo.external \
--kid hosted-demo-client-2 \
--public-key "$CLIENT_PUBLIC_KEY_B64URL" \
--route-id acme.demo.orders.read \
--subject-exact external:jwks:hosted-demo-client-2 \
--required-key-binding software
  • Existing issuers, keys, routes, and sources are preserved by default.
  • A duplicate kid or matching selector is rejected unless you pass --replace-existing.
  • The command writes full merged bundle files you can review before publishing.

Step 3 — Verify the "before" state denies

Before publishing, the new client's key is unknown, so the adapter denies it. With the local adapter this is source_*_mismatch or an unknown-key denial.

Step 4 — Publish / mount the merged bundles

  • Locally: point the adapter at the merged files (or replace the mounted files); the adapter reads fresh bundle material on reload.
  • Deployed adapter: refresh the mounted signed bundle objects through your reviewed deployment process, then roll or signal the adapter so the projected files are re-read — without changing the adapter image. See Bundle refresh & key lifecycle.

Step 5 — Verify the "after" state allows

The new client signs GET /orders with its key and the adapter now allows it. Replay, tampered path, wrong audience, and wrong route still deny.

Guardrails to remember

  • Append-only by default. Destructive replacement is an explicit --replace-existing.
  • Local removal exists. Use trustplane bundle remove-source with --confirm-remove to remove a trusted key and matching route source selector while preserving unrelated entries.
  • Local revocation metadata is optional. Add --revoke when you want the reviewed output to append local revocation metadata for the removed key/source. Managed hosted revocation remains future Control work.
  • No persistent principal is created. This is policy-based acceptance, not enrollment; persistent principals, approvals, and managed revocation are out of scope for this runtime.

Why this matters

In an API-key world, onboarding a partner often means a config/secret change and a deploy. Here it is a reviewed bundle publish that the running adapter picks up. That is the difference between "trust is code you ship" and "trust is data you publish."

Protect your first API · → Run the acceptance gate