Skip to content

docs(evidence): teach relying-party integration in application code - #653

Merged
jeremi merged 3 commits into
feat/evidence-client-libraryfrom
docs/evidence-client-tutorial
Aug 6, 2026
Merged

docs(evidence): teach relying-party integration in application code#653
jeremi merged 3 commits into
feat/evidence-client-libraryfrom
docs/evidence-client-tutorial

Conversation

@jeremi

@jeremi jeremi commented Aug 5, 2026

Copy link
Copy Markdown
Member

Stacked on #649 (feat/evidence-client-library). Review that one first; this branch adds one page and one sidebar line on top of it.

Why

verify-an-assertion-as-a-consumer already states the verification boundary, but it states it abstractly: "put the boundary in application code" in six steps, with no program. A relying party integrating for the first time still has to decide, in its own codebase, where the trusted key set lives, where the request expectations come from, and what it does with a subject binding the first time it sees one. Those are exactly the decisions that go wrong quietly.

This page walks that journey once, concretely, with the Python client from #649.

What it teaches

docs/site/src/content/docs/tutorials/request-evidence-from-an-application.mdx, doc_type: tutorial, persona consumer or verifier. It continues the first-evidence-assertion project rather than starting a new one.

The spine, in order:

  1. Give the application its own identity. evidencectl access policy add plus access client add --generate-local-key, reusing the commands from control-who-can-request-evidence verbatim. The generated client file sets evidenceAudience, which is what makes the audience expectation in step 4 deterministic.
  2. Pin the keys the application trusts. evidencectl jwks --out trusted-issuer-keys.json is the local stand-in for the independent channel production requires. The page says so rather than implying the local shortcut generalizes.
  3. Build the client from source. Same recipe as crates/registry-evidence-client-py/tests/python/bootstrap.py, because nothing here is published to PyPI.
  4. Read the definitions once, and keep the answer. Discovery is read while authoring the procedure, not per request, which is what registry-evidence-client/src/definitions.rs:1-10 says the document is for.
  5. Pin the procedure. A pure offline transformation of the saved discovery document into a procedure.json the application owns. No network call and no client construction, because pinning is something you do to a document you already reviewed. Nothing is hand-copied, so no reader transcribes a 64-hex digest. expected_outputs stays a hand-written literal on purpose: a concept's published form and a verification expectation's form are separate vocabularies (definitions.rs:205-219 folds controlled-code, controlled-category, and bounded-decimal onto string), so deriving one from the other would work for this requirement and mislead on the next. The generator checks the concept identifiers against discovery, so a deployment that stops publishing one fails at review time with a named error rather than at verification time.
  6. Write the procedure into the application. One python fence that loads procedure.json and holds no discovery client at all. prepare builds the policy and the nonce before the answer exists; request_and_verify is the only thing that returns a payload; answers are read into a dict keyed by providesValueFor, never by position.
  7. Refuse before reading. Tampering with the pinned binding, and editing the configuration_revision in procedure.json, both fail closed with a policy code and print nothing about the subject.

The page also states, in prose, that regenerating procedure.json is a review step and not a retry: keep the reviewed copy, write a new one, diff them, and treat a changed evidence_type, issued_by, or concept set as the question itself having moved.

It also documents the client's error hierarchy as a table (all eight kinds), including the hazard the crate README calls out: 401/403/429 map to denied whatever code the body carries, and every other non-2xx maps to protocol. RuntimeError/ValueError sit outside EvidenceClientError.

Verification

I ran the whole journey end to end on this machine, from a fresh project, and then replayed it a second time in a clean directory by extracting the page's own fences programmatically and running them verbatim. Every documented command output in the page is a real transcript, not a reconstruction.

Proven along the way, and reflected in the prose:

  • Discovery is authenticated: it returns 401 without a token, so client.discover() is what makes it work.
  • The private_key_jwt assertion audience defaults to the token endpoint, which matches what evidencectl dev configures for Mint.
  • person-123 (adult) and person-456 (minor) both return verified answers; the accept-first-use to pinned progression persists to subject-bindings.json under umask 077.
  • A tampered pinned binding and an edited configuration_revision both fail verification, with nothing read. Both surface the same generic message, which is deliberate: registry-evidence-verifier/src/verifier.rs:795-823 collapses every policy mismatch onto one error so verification does not reveal which hidden comparison failed.
  • The pinning fence is a real offline transformation. Extracted from the page and re-run in a directory holding nothing but the saved discovery.json (no client file, no key set, no built module, no project), it exits 0 and reproduces the documented procedure.json byte for byte. The fence names no client, no URL, and no HTTP library.
  • evidencectl dev clean retains the reader's own files (age_check.py, the trusted key set, the bindings file, the built module, the client private key), so the page's teardown is safe to follow.
  • One correction found by replay and fixed: before dev has ever run, request prepare without --client fails on the missing private directory, not on the missing client. The fence that documents the missing-client message now sits after dev --detach, where it reproduces.

Docs gates, all run in this worktree:

  • npm run check (full chain) exit 0
  • npm test 310 pass / 0 fail
  • check:content "Frontmatter check passed."
  • check:style 0 errors, 0 warnings, 0 suggestions in 131 files
  • check:markdown 0 errors

Deliberately not registered in EVIDENCE_TUTORIALS

docs/site/scripts/check-evidence-tutorials.sh replays a registered tutorial's sh fences in a container that mounts the repo read-only and injects only the three Evidence binaries. This page needs a Rust build of a Python extension module inside that container, which the sandbox cannot do by design. Registration is opt-in and no gate requires it, so the page ships unregistered and verified by hand.

Follow-up worth deciding on: whether the executable-tutorial gate should grow a variant that can build the client bindings, or whether client-integration tutorials stay hand-verified. I did not change the gate.

Scope

Entirely docs/site/: one new page, one sidebar entry in astro.config.mjs. No crate, product-spec, or release changes.

Unrelated, flagging rather than fixing

docs/evidence-ux-reset carries a committed sh fence in docs/site/src/content/docs/tutorials/request-evidence-as-sd-jwt-vc.mdx that fails /bin/sh -n, so npm test in docs/site is red on that branch. It is not reachable from this stack and I did not touch it.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e02b8c7431

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/site/src/content/docs/tutorials/request-evidence-from-an-application.mdx Outdated
Comment thread docs/site/astro.config.mjs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2394081e1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/site/src/content/docs/tutorials/request-evidence-from-an-application.mdx Outdated
Comment thread docs/site/src/content/docs/tutorials/request-evidence-from-an-application.mdx Outdated
Comment thread docs/site/src/content/docs/tutorials/request-evidence-from-an-application.mdx Outdated
Comment thread docs/site/src/content/docs/tutorials/request-evidence-from-an-application.mdx Outdated
Comment thread docs/site/src/content/docs/tutorials/request-evidence-from-an-application.mdx Outdated
@jeremi
jeremi force-pushed the feat/evidence-client-library branch from 8f041d1 to f400f54 Compare August 6, 2026 05:09
@jeremi
jeremi force-pushed the docs/evidence-client-tutorial branch from 2394081 to cec952e Compare August 6, 2026 05:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cec952e4dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread docs/site/src/content/docs/tutorials/request-evidence-from-an-application.mdx Outdated
@jeremi
jeremi force-pushed the docs/evidence-client-tutorial branch from cec952e to 20fddb2 Compare August 6, 2026 05:22

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 20fddb2cc7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

jeremi added 3 commits August 6, 2026 13:59
The consumer tutorial states the verification boundary abstractly. A relying
party still has to decide where the trusted key set, the request policy, and
the subject binding live in its own program. Walk that journey once with the
Python client: give the application its own identity, pin the issuer keys out
of band, build the expectations while the answer is still unknown, and read a
value only after offline verification returns.

The page is deliberately not registered in EVIDENCE_TUTORIALS: the executable
tutorial gate mounts the repo read-only and injects only the Evidence
binaries, so the Python extension module cannot be built inside it.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Copying a 64-hex configuration revision into source by hand is a poor first
experience and invites the reader to keep re-copying it whenever verification
starts failing. Save the discovery document instead, transform it offline into
a procedure.json the application owns, and load that file at startup.

The pinning act stays explicit and still happens before any answer is read; it
is only automated instead of transcribed. Regenerating is documented as a
review step, with the diff to look at, not a retry.

expected_outputs stays hand-written: a concept's published form and a
verification expectation's form are separate vocabularies, so deriving one
from the other would work for this requirement and mislead on the next. The
generator checks the concept identifiers against discovery so a deployment
that stops publishing one fails at review time.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
Review surfaced eight defects, each confirmed against source:

- the policy and client ids collided with the access-control tutorial, so
  authoring refused for a reader who had followed the other page; both pages
  now carry their own ids and this one says why they compose
- the source clone was unpinned, so a reader could build the client from a
  tree that need not match the installed evidencectl; it now pins the tag of
  the installed version
- the review step accepted a subset of the published concepts, which the
  verifier rejects at request time; it now requires the exact set
- the application hard-coded the selector profile instead of reading the
  shape it had reviewed
- the binding-stability prose named only subject and audience, while the MAC
  also covers purpose, role, selector profile, and the deployment's binding
  key and key version
- the bindings store dropped a concurrent run's entry, and its replacement
  stays owner-only whatever umask the shell carries
- the page never restarted the registry the prerequisite leaves stopped, and
  evidencectl dev reports ready without reaching the source
- the error-mapping prose implied any body code maps, where the contract
  honors a code only under a status registered for it

Verified by replaying all thirty-one fences against binaries built from this
checkout: every documented output matches apart from the deliberate revision
placeholder. docs/site npm test and npm run check pass.

Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
@jeremi
jeremi force-pushed the docs/evidence-client-tutorial branch from 20fddb2 to 4fb65fd Compare August 6, 2026 07:21
@jeremi
jeremi merged commit 4fb65fd into feat/evidence-client-library Aug 6, 2026
2 checks passed
@jeremi
jeremi deleted the docs/evidence-client-tutorial branch August 6, 2026 07:24
@jeremi

jeremi commented Aug 6, 2026

Copy link
Copy Markdown
Member Author

Consolidated into #649: these three tutorial commits are now on feat/evidence-client-library, which was a clean fast-forward, so the client library and the tutorial that exercises it ship as one change. GitHub marked this merged automatically once its head became reachable from its base.

All ten review threads here were answered and resolved first: eight fixed, two refuted on the evidence (both cited commits were rebase-superseded and are not present in the repository; every commit on the head carries a sign-off). Review continues on #649.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4fb65fdbb7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -0,0 +1,704 @@
---
title: Request Evidence from your application

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the required DCO sign-off

Fresh evidence after the earlier threads: the reviewed replacement commit df805ceb9c51346a41fc7f67b6353d1d2ec26833 itself has no Signed-off-by trailer, so it still violates the mandatory DCO policy and cannot pass the DCO gate; recreate the commit with the author's sign-off before merging.

AGENTS.md reference: AGENTS.md:L190-L190

Useful? React with 👍 / 👎.

# keeping the identifiers above.
[subject] = definition["subjects"]
selector = subject["selector"]
fields = {field["name"] for field in selector["fields"]}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retain complete selector field metadata

Fresh evidence after the earlier subject-shape thread: the replacement now reduces each selector field to its name, so changing person_id from a string to another type, or changing its byte bounds while retaining the name and profile, still passes this check and produces the same published_shape apart from the revision. An application can therefore accept what looks like a revision-only update and continue sending values that the new selector rejects, contradicting the page's claim that a changed request shape cannot pass review; compare and retain the complete field objects, including type and constraints.

AGENTS.md reference: docs/site/AGENTS.md:L25-L28

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant