Skip to main content
A refund is about to go out. Someone has to say yes to it, and “yes” has to mean the refund that is sent, not a summary of it. The failure everyone has seen is the one where a person approves a description, the system rebuilds the request after the yes, and what goes out is not what was read: a different amount, a different account, the same approval. You want the approval bound to the bytes. The person sees the exact request, their yes is recorded against exactly that, and that is what is sent. If anything in the request changes, by one field, you want the question asked again, not answered by the old yes. And you want the record to show which bytes were approved and which were sent. Obversa does this with the request as the question’s input. The question’s digest covers every byte of the payload, the answer is recorded against that digest, and the send uses the payload the person read. Ask again with the same bytes and the step finds the answer. Ask with different bytes and there is no answer to find, so the run stops on a new question. This file is one two-step run, approve and execute, made three times over one callbacks client: asked, approved and sent, asked again.

Run it

Set the project up as Installation describes. Copy the file with requests/ beside it and run it from that directory. No model runs, so no sign-in is needed. Set PAYMENTS_URL to your payments endpoint, or leave the fictional default:
Terminal
The output below is the proof’s offline run, with a stand-in for curl that records the call instead of making it.
Output, from the offline proof
Three passes, one send. The first pass asked and nobody had answered, so nothing was sent. The person answered yes to those bytes. The second pass, with the same payload, found the answer on the record and sent it. The third pass carried the same request with the amount changed from 48.00 to 480.00; its digest was new, no answer applied, and it stopped on a fresh question. One question is pending: the changed one.

The file

The refund is the approval’s input, and the send uses the same object:
examples/use-cases/ops/approve-the-exact-payload.ts (excerpt)
Every question a run asks goes through the run’s callbacks client, and so does every answer. One client serves all three passes here, so what one pass asked and what the person answered is there for the next:
examples/use-cases/ops/approve-the-exact-payload.ts (excerpt)
The person’s console is played by the file, so it runs offline: it lists the pending question, claims it, and submits { approved: true } against the request’s digest. In use that is a page that shows the payload and takes the yes, and the client is a stored one, so the answer survives the process that asked; Callback gates has both. The digest is what makes the third pass ask again: it covers the gate, the question text, the answer’s schema and the input bytes, and nothing else, so restyling the page never invalidates an answer and changing what is asked always does.
examples/use-cases/ops/approve-the-exact-payload.ts

The team’s shape

What the run did

The proof runs the file against a stand-in for curl and checks what the page describes: three runs, one call to payments, the body of that call equal byte for byte to the approved payload and not to the changed one, and the changed payload left as the one pending question. Obversa recorded each pass as its own event log, so the first shows the question with its digest, the second shows the approval found and the send, and the third shows a different digest and a new question. The rule the file keeps is small and does all the work: what the person read is the input, the input is in the digest, and the send uses the input. There is no step where a summary is approved and a request is built afterwards. Proof-bound acceptance and approval takes the same idea further, binding a decision to a proof, a graph and a workspace as well as to the bytes.

Next steps