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
kidor 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-sourcewith--confirm-removeto remove a trusted key and matching route source selector while preserving unrelated entries. - Local revocation metadata is optional. Add
--revokewhen 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."