Request Notarization
QHx Notary links application requests to workload identity statements. At the
signRequest level it also signs a receipt containing the captured request and
response. An operator can use qhx curl to verify those artifacts while making
an HTTP request.
Prerequisites
Section titled “Prerequisites”The examples below assume an OpenAI-compatible chat service behind a QHx
notary, reached through a client proxy listening on localhost:8081. Run the
commands where that client proxy and the SPIFFE Workload API are available.
The backend must provide the model named in the request.
You need:
- A QHx Proxy with notarization enabled and the
openaiandnotary-querymiddleware configured for the HTTP listener. - Client and server proxy identities that the deployment’s trust and authorization rules permit.
- A
qhxexecutable with access to the SPIFFE Workload API. Use--spiffe-socket-pathif its socket differs from the default. - Network access from the CLI to the service and its
/.qhx/artifact and certificate endpoints. HTTP verification uses those endpoints and the Workload API’s trust bundles.
See qhx curl for flags and defaults. Check your
installed CLI’s --version and --help when following these examples.
Choose a notarization level
Section titled “Choose a notarization level”| Level | Workload statement | Request and response |
|---|---|---|
workload | Signed and logged | No request receipt |
logRequest | Signed and logged | Logged without a receipt signature |
signRequest | Signed and logged | Logged in a signed receipt |
The workload statement identifies the workload and includes Kubernetes pod metadata, labels, annotations, and the image references declared in its pod specification. A tag in the pod specification remains a tag in this report; the report does not automatically turn it into an image digest.
For HTTP chat completions, receipts capture the method, path, body, response
status, and selected headers (Content-Type and User-Agent). Choose the
level according to the evidence you need and the sensitivity of the data being
recorded. Request signing and storage add work; measure their impact with your
own request sizes and workload.
Inspect workload identity
Section titled “Inspect workload identity”qhx curl -n workload --print-workload \ -X POST -H 'Content-Type: application/json' \ -d '{"model":"llama3.2:1b","messages":[{"role":"user","content":"Hello"}]}' \ http://localhost:8081/v1/chat/completionsThe CLI prints the response body to standard output. After verifying the workload statement, it prints a human-readable identity report to standard error.
Log the request
Section titled “Log the request”qhx curl -n logRequest \ -X POST -H 'Content-Type: application/json' \ -d '{"model":"llama3.2:1b","messages":[{"role":"user","content":"Hello"}]}' \ http://localhost:8081/v1/chat/completionsThe notary records the request and response and returns a request ID. The CLI
still verifies workload identity. logRequest does not provide a signed
request receipt.
Verify a signed receipt
Section titled “Verify a signed receipt”qhx curl -n signRequest --print-receipt \ -X POST -H 'Content-Type: application/json' \ -d '{"model":"llama3.2:1b","messages":[{"role":"user","content":"Hello"}]}' \ http://localhost:8081/v1/chat/completions \ > response.json 2> receipt-report.txtThe CLI verifies the workload statement and receipt, checks their association, and compares the receipt’s captured HTTP data with the request and response. Keep validation enabled for this operation.
response.json contains the service’s response body. receipt-report.txt
contains the human-readable receipt report and any diagnostics. A printed
report is not a standalone signed artifact or a JSON export. qhx curl
performs online verification; it does not provide an offline verification
command. Long-term evidence retention also requires the signed artifacts,
certificates, trusted verification material, and a verification policy.
The response headers identify the artifacts using base64url-encoded values:
Qhx-Workload-Id: <base64url workload statement ID>Qhx-Request-Id: <base64url request ID>The request ID is present for logRequest and signRequest. Use the values
returned by the service when looking up artifacts; the placeholders above are
not IDs to send to a server.
Configure the notary
Section titled “Configure the notary”The following is a QHx Proxy configuration template for an HTTP central notary. Replace the target URL and both identity patterns with the addresses and identities authorized by your deployment. Mount the SPIFFE Workload API socket and writable storage at the configured paths.
spiffe: workload_socket_path: unix:///spiffe-workload-api/agent.socknotary: enable: true database: path: /var/lib/qhx-notary/notary.dblisteners: - name: central-http address: 0.0.0.0:8081 protocol: http mode: central source: spiffe_ids: - '^spiffe://client\.example/ns/ai-chat/sa/llm-client/.*$' target: url: https://app-server.ai-chat.svc.cluster.local:8081 spiffe_ids: - '^spiffe://server\.example/ns/ai-chat/sa/ollama-server/.*$' middlewares: - type: notary-query - type: openaiSupply the YAML through the proxy’s QHX_PROXY_CONFIG environment variable or
its -config file option. The openai middleware handles
POST /v1/chat/completions; other endpoints are rejected by default.
The notary also needs Kubernetes permission to read the pod metadata used in
workload statements. The configuration alone does not create storage, RBAC,
proxy sidecars, or application routes.
Protect and interpret the evidence
Section titled “Protect and interpret the evidence”Use persistent storage if artifacts must survive pod replacement. Protect notary data and backups according to the sensitivity of request and response bodies. Configure retention through your storage and operational process; a writable database path does not make storage immutable or guarantee retention.
A valid signature links the notary’s statement to its signing identity and protects the signed contents against modification. It does not prove that an AI response is correct or independently attest model weights, training data, or every event in an application’s history. Decide which claims satisfy your own audit requirements and retain their verification material accordingly.
If HTTP validation fails, check that the request reached the configured notary,
that the CLI can access the Workload API and /.qhx/ endpoints, and that their
trust material matches the intended deployment. --validate=false permits a
missing workload statement; it is not a remedy for failed verification of a
QHx-protected service.
For MQTT, the CLI only decodes and displays live notary messages without verifying signatures. See MQTT artifact inspection before using that output as evidence.