Skip to content

5. Send and audit the first managed request

The request now crosses the real Auth boundary. Auth checks the caller, short-lived passport, proof bound to this request, route policy and replay state before forwarding to the identified private upstream. Control Audit provides the attributed runtime decision; the upstream’s own business outcome is separate.

Previous: Create a client and grant. Next: Call the same API from Python, then change access and troubleshoot.

  1. In Client Vault, select the exact site, client, key and active granted route. Inspect the canonical signing profile and Inspect command. The Copy and run smoke command is available only when that key’s profile is active for the acknowledged target version. It reads TRUSTPLANE_CLIENT_PRIVATE_KEY_FILE; it must not contain private key bytes. Use the checksum-verified CLI v0.2.5 release for this tutorial.

  2. From a trusted workload environment, use the persistent client-private-key from chapter 4. Copy the values for issuer, KID, subject, trust domain, audience, route ID and Auth authority from Inspect command for your active profile. Replace every angle-bracket value below. For this exact bodyless GET /tutorial/echo route, the following single Python command runs the released CLI once in its JSON mode, sends that same signed request, and prints only its HTTP status and JTI. It holds the CLI JSON (including the bearer token, proof and private key field) in process memory; it never prints or writes that JSON. Do not use shell tracing, a public terminal recorder, a redirected output file, or an upstream URL that bypasses Auth. A body-bearing operation needs the exact body-file handling shown in Control’s generated command and is outside this read-only example.

    Terminal window
    export TRUSTPLANE_CLIENT_PRIVATE_KEY_FILE="$PWD/client-private-key"
    export TP_AUTH_BASE_URL='https://<Auth Site authority>'
    export TP_ISSUER='<issuer>' TP_KID='<kid>' TP_SUBJECT='<subject>'
    export TP_TRUST_DOMAIN='<trust domain>' TP_AUDIENCE='<audience>'
    export TP_ROUTE_ID='<route ID>'
    test -r "$TRUSTPLANE_CLIENT_PRIVATE_KEY_FILE" || exit 1
    python3 - <<'PY'
    import base64
    import json
    import os
    import subprocess
    from urllib.error import HTTPError
    from urllib.parse import urlsplit
    from urllib.request import HTTPRedirectHandler, Request, build_opener
    class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, *args, **kwargs):
    return None
    base = urlsplit(os.environ["TP_AUTH_BASE_URL"])
    if base.scheme != "https" or not base.netloc or base.path not in ("", "/") or base.query or base.fragment or base.username:
    raise SystemExit("Use only the exact HTTPS Auth Site authority")
    args = ["./trustplane", "sign",
    "--private-key-file", os.environ["TRUSTPLANE_CLIENT_PRIVATE_KEY_FILE"],
    "--issuer", os.environ["TP_ISSUER"], "--kid", os.environ["TP_KID"],
    "--subject", os.environ["TP_SUBJECT"],
    "--trust-domain", os.environ["TP_TRUST_DOMAIN"],
    "--audience", os.environ["TP_AUDIENCE"],
    "--route-id", os.environ["TP_ROUTE_ID"],
    "--key-binding", "software", "--method", "GET",
    "--scheme", "https", "--authority", base.netloc,
    "--path", "/tutorial/echo", "--query", "", "--ttl", "5m"]
    signed = json.loads(subprocess.run(args, capture_output=True, text=True,
    check=True).stdout)
    headers = signed["headers"]
    token = headers["Authorization"].removeprefix("Bearer ")
    claims = json.loads(base64.urlsafe_b64decode(token.split(".")[1] + "=="))
    jti = claims["jti"]
    if not isinstance(jti, str) or not jti or signed["method"] != "GET" or signed["url"] != os.environ["TP_AUTH_BASE_URL"].rstrip("/") + "/tutorial/echo":
    raise SystemExit("CLI output does not match the selected GET route")
    request = Request(signed["url"], headers=headers, method="GET")
    try:
    with build_opener(NoRedirect()).open(request, timeout=20) as response:
    status = response.status
    except HTTPError as error:
    status = error.code
    print("HTTP", status)
    print("Audit correlation JTI:", jti)
    PY

    The CLI v0.2.5 JSON result has headers, method and url; its --curl mode emits a ready-to-run shell command containing the bearer token, so this example deliberately does not use --curl or eval. The JTI decode is for correlation only; Auth verifies the token. The transport refuses redirects so it cannot forward signed headers to another destination.

  3. Send a separate unsigned request to the same protected method and path. It should be denied at Auth, normally 401 missing_bearer_token. For the signed request, record the actual HTTP status, Auth decision and upstream result. Auth allowed means the authorization boundary passed; it does not prove the upstream business action succeeded.

    Terminal window
    curl --silent --show-error --output /dev/null --write-out '%{http_code}\n' \
    'https://<Auth Site hostname from active profile>/tutorial/echo'
  4. In Control → Audit, select the exact site and time window, then inspect the attributed decision. Compare the printed JTI from this same CLI-generated request to the jti in Raw evidence → Sanitized raw evidence. Confirm the subject, method, canonical path, route ID, Auth outcome, target bundle version and upstream outcome from the fields actually present. The current raw event does not include the client key ID or adapter version; verify key and active target separately in Client Vault and Auth Site status. An HTTP status alone is not Audit correlation. Early gateway denials can happen before a passport is parsed and may have no JTI; correlate those by site, time, method and reason without claiming an attributed client decision. If the signed call is redirected, fails before reaching Auth, or its Audit row is absent, do not infer authorization from the locally decoded JTI.

Open this walkthrough on YouTube

The 14 October 2026 first-run call returned HTTP 200 and the private sample echo response. Its local jti exactly matched the allowed, verified Control Audit event for the same site, caller, GET /tutorial/echo route and upstream HTTP 200. A separate unsigned call returned 401; its Audit event reported missing_bearer_token, route not evaluated, and upstream not reached. No customer system was involved.

If using a product-owned mock API as the private upstream, identify it as a sample upstream. It is still a deployed API reached through the real Auth Site; it is not a customer’s banking system.

Next: Show how a policy or key change propagates and how to diagnose a denial.