Brownfield APIs
Most APIs worth protecting already exist, and in an API-key world onboarding a new partner to one of them usually means a config/secret change and a deploy. TrustPlane Auth makes that a data change instead: you 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
Section titled “The idea”flowchart LR K["New client: gen-key"] --> M["bundle merge-source<br/>(append key + source rule)"] M --> P["Publish/mount merged bundles"] P --> R["Adapter reloads bundle"] R --> OK["New client's signed GET /orders allowed"] style M fill:#1f2937,stroke:#818cf8,color:#fff
The adapter image, Deployment, and Service all stay exactly the same. Only the bundle changes.
Step 1 — Generate the new client’s key
Section titled “Step 1 — Generate the new client’s 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
Section titled “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
Section titled “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 or mount the merged bundles
Section titled “Step 4 — Publish or 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 and key lifecycle.
Step 5 — Verify the “after” state allows
Section titled “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
Section titled “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 is future TrustPlane 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.
Next steps
Section titled “Next steps”- Protect your first API — the tutorial that sets up the route this page extends.
- Run the acceptance gate — verify the full allow/deny matrix.
- Brownfield adapter — how the adapter fronts existing APIs without code changes.