Skip to content

Protect your first API

You have an existing HTTP service and you want only verified, proof-bound requests to reach it — without changing the service itself. This tutorial puts the TrustPlane adapter in front of your API to do exactly that. It takes about 10 minutes, all local.

flowchart LR
  C["Your client"] -->|"passport + proof"| A["trustplane-adapter"]
  A -->|"verified only"| U["Your existing API"]
  A -. "401 / 403 / deny" .-> C

Use the verified CLI archive from Install and artifacts, or build from a TrustPlane Auth source checkout:

Terminal window
make build

Step 2 — See the adapter behavior with the demo

Section titled “Step 2 — See the adapter behavior with the demo”

Before wiring your own service, watch the adapter prove the three core outcomes:

Terminal window
make demo-adapter

You will see: a valid passport reaches the upstream; a missing passport returns 401; a wrong audience returns 403. This is the exact behavior you’ll get in front of your own API.

Step 3 — Build trust material and a route policy

Section titled “Step 3 — Build trust material and a route policy”

Create an authoring config describing your route and the client source you’ll allow, then build the bundle:

Terminal window
trustplane bundle build --config bundle.config.json --out trustplane.bundle.json

The route entry decides what’s allowed (issuer, trust domain, subject, key binding) and how fresh the policy must be (freshness_class). See Bundle policy for the full shape. Generate the client’s key with trustplane gen-key and put its public key in the trust material.

Step 4 — Run the adapter in front of your service

Section titled “Step 4 — Run the adapter in front of your service”

Point the adapter’s --upstream at your existing API and mount the bundle:

Terminal window
./bin/trustplane-adapter \
--upstream http://127.0.0.1:8080 \
--policy-bundle ./trustplane.bundle.json \
--route-policy-mode request \
--proof-mode transcript-v1

--route-policy-mode request lets one adapter protect many routes by matching the request method/path against the bundle’s method + path_template.

Sign a request (no broker needed) and send it:

Terminal window
trustplane sign \
--private-key "$CLIENT_KEY" \
--method GET --path /orders \
--audience acme.demo.orders \
--route-id acme.demo.orders.read \
--curl

Run the emitted curl. It succeeds. Now tamper with the path (call /orders-tampered with the same proof) and you’ll get request_binding_mismatch. Send the same valid request twice and the second returns jti_replay.

Try this Expect
No Authorization header 401
Wrong --audience 403
Tampered path/route after signing request_binding_mismatch
Replay the same signed request jti_replay
Route not in the bundle bundle_route_missing
  • Your API is now reachable only through verified, short-lived, request-bound calls.
  • You added zero verification code to the upstream.
  • Adding another client later is a bundle change, not a redeploy — see Brownfield APIs for that flow.
  • The same adapter can accept local software, EC2-style, SPIFFE/SPIRE, or automation callers when each source is explicitly allowed. See the use cases overview.

When you’re ready to run this in front of a live API, continue to the deployment overview.