Unreleased documentation. These pages follow the main branch and can change before the next release. For supported guidance, use v0.15.2.
Assert a role-bound relationship
For the assertion provider
Answer, “Is this candidate registered as a parent of this child?” Unlike your first Evidence Gateway assertion and the governed-value follow-up, this standalone tutorial binds two people in distinct roles. Evidence Gateway must not let a caller omit a role, repeat one role, or swap its selector field.
Start a relationship registry
Section titled “Start a relationship registry”Create a working directory and save this server as registry.py:
mkdir role-bound-relationshipcd role-bound-relationshipimport jsonfrom http.server import BaseHTTPRequestHandler, ThreadingHTTPServerfrom urllib.parse import unquote, urlsplit
PEOPLE = {"child-123", "parent-456", "adult-789"}REGISTERED_PARENTS = {("child-123", "parent-456")}
OPENAPI = { "openapi": "3.1.0", "info": {"title": "Tutorial relationship registry", "version": "1.0.0"}, "servers": [{"url": "http://127.0.0.1:8002"}], "paths": {"/children/{child_id}/candidate-parents/{candidate_id}": {"get": { "operationId": "getParentRelationship", "parameters": [ {"name": "child_id", "in": "path", "required": True, "schema": {"type": "string"}}, {"name": "candidate_id", "in": "path", "required": True, "schema": {"type": "string"}}, ], "responses": {"200": { "description": "A registered parent relationship decision", "content": {"application/json": {"schema": { "type": "object", "required": ["relationship_confirmed"], "properties": {"relationship_confirmed": {"type": "boolean"}}, }}}, }}, }}},}
class Registry(BaseHTTPRequestHandler): def do_GET(self): path = unquote(urlsplit(self.path).path) if path == "/openapi.json": return self.send_json(OPENAPI) parts = path.strip("/").split("/") if (len(parts) == 4 and parts[0] == "children" and parts[2] == "candidate-parents"): child_id, candidate_id = parts[1], parts[3] if child_id in PEOPLE and candidate_id in PEOPLE: return self.send_json({ "relationship_confirmed": (child_id, candidate_id) in REGISTERED_PARENTS, }) self.send_error(404)
def send_json(self, value): body = json.dumps(value).encode() self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body)
print("Registry listening on http://127.0.0.1:8002", flush=True)ThreadingHTTPServer(("127.0.0.1", 8002), Registry).serve_forever()Start it in one terminal and leave it running:
python3 registry.pyThe synthetic registry knows three people and one registered relationship. Its endpoint returns only the relationship decision for a known pair. A production source would need governance that defines what “registered parent” means and who can maintain that relationship.
Create the Evidence Gateway project
Section titled “Create the Evidence Gateway project”In another terminal, create an editable project from the registry’s OpenAPI description:
evidencectl new parent-relationship \ --openapi http://127.0.0.1:8002/openapi.json \ --profile localcd parent-relationshipquestions/parent-relationship.yaml
Section titled “questions/parent-relationship.yaml”Create the question definition:
id: parent-relationshipquestion: Is the candidate registered as a parent of the child?purpose: relationship-checksubjects: - role: child selector: child_id - role: candidate-parent selector: candidate_idsource: operation: getParentRelationship facts: - name: relationship_confirmed path: /relationship_confirmed combine: exactly-one collectionBounds: {}answers: - concept: relationship_confirmed type: booleanderivation: derivations/parent-relationship.rhaidisclosure: allow: [relationship_confirmed]subjects declares the complete role set. Each role maps to one required OpenAPI path parameter.
The child and candidate are therefore part of the governed question, not interchangeable string
arguments. The source decision is the only fact that reaches the derivation.
derivations/parent-relationship.rhai
Section titled “derivations/parent-relationship.rhai”Create the answer logic:
fn answer(facts, selectors, context) { #{ relationship_confirmed: required(facts.relationship_confirmed, "relationship_missing") }}This source already owns the reviewed relationship decision, so the derivation maps that fact to the one governed answer. Evidence Gateway still validates the source response, answer form, disclosure, and both subject bindings before signing.
Start the project
Section titled “Start the project”evidencectl dev --detachEvidence Gateway ready at http://127.0.0.1:8080Mint ready at http://127.0.0.1:8081Bind both subjects to the request
Section titled “Bind both subjects to the request”Prepare one request with both role-qualified subjects:
evidencectl request prepare parent-relationship \ --purpose relationship-check \ --subject child:child_id=child-123 \ --subject candidate-parent:candidate_id=parent-456 \ --name parent-relationshipFor a multi-subject question, every --subject uses role:field=value. Preparation rejects a
missing role, duplicate role, unknown role, or field that does not match that role’s selector.
Send the prepared request across the HTTP boundary:
curl --silent --show-error \ --config .evidence/requests/parent-relationship/authorization.curl \ --request POST \ --url http://127.0.0.1:8080/v1/evidence \ --header 'Content-Type: application/json' \ --header 'Accept: application/jose+json' \ --data-binary @.evidence/requests/parent-relationship/request.json \ --output parent-relationship.jws.jsonVerify before reading:
evidencectl verify parent-relationship.jws.json \ --context .evidence/requests/parent-relationship/verification.json \ --output parent-relationship.verified.jsonVERIFIEDpython3 -m json.tool parent-relationship.verified.jsonThe verified assertion has two pseudonymous subject bindings, one for child and one for
candidate-parent, plus this supported value:
{ "providesValueFor": "urn:registrystack:evidence:local:concept:parent-relationship:relationship_confirmed", "value": true}It does not disclose child-123 or parent-456. The bindings let an authorized verifier relate
the answer to the requested subjects without turning the assertion into a copy of either record.
Inspect the audit and clean up
Section titled “Inspect the audit and clean up”evidencectl dev stopevidencectl audit show --last-operationevidencectl dev cleanThe audit identifies the authorized question, purpose, requester pseudonym, and disclosed concept. It does not record either source identifier or the boolean value.
Return to the registry terminal and press Ctrl+C.