# WAI Extension: Quantum Providers (`wai.quantum.provider/1`)

Status: **Draft.** Client, reference provider and conformance checker:
`wai-quantum-qpu` (`src/open_provider.rs`, `src/reference.rs`,
`src/conformance.rs`); vectors: `wai-quantum-qpu/conformance/provider-v1.json`.
The engine's side of a provider job: `wai-quantum/src/engine/provider.rs`.

Keywords **MUST**, **MUST NOT**, **SHOULD** and **MAY** are RFC 2119/8174.

## 0. What this extension adds

Bring your own quantum. WAI provides the engine, the free job service and the
verifier at no cost, and never pays for or resells time on anyone's hardware.
A person connects their own quantum hardware, provider accounts, on-premises
devices or simulators, and every surface treats a provider's result as it
treats a WAI result: planned, its cost shown before it is submitted, checked,
and sealed in a receipt.

This text is the protocol a device speaks so that it can be brought without a
client written for it: JSON messages, one request and one answer at a time,
over HTTP or to a local process (§2). Six operations (§5) describe the
device, estimate a job, submit it, follow it, read its counts and cancel it.
A program travels in the device's own native gates, with every angle an exact
fraction of a turn (§4), so the program the device runs is the one the client
compiled and checked.

A provider is the program or service that answers the messages. A device is
what it runs programs on: a processor, an emulator or a simulator. A client
is what sends the messages; a host is the client's process, which holds the
person's credential and signing key (the command line, the desktop app).

## 1. Principles

1. **The credential stays with the person.** A host keeps a provider's
   credential in the operating system's keychain (or, where there is none, a
   file only its owner can read, labelled as such) and sends it only to that
   provider (§2). A credential MUST NOT appear in a browser page, a log, a
   receipt, a command line, an answer the host prints, or the hosted job
   service. The job service never routes a job to a provider and never takes
   a credential: a request that names a provider is refused there.
2. **Cost before submit.** A client MUST show a job's estimate (§5.2) before
   it submits the job, and MUST NOT submit a job that is not free without the
   person's explicit confirmation (§7). An unknown cost is not free.
3. **Exact programs.** A client compiles a circuit exactly to the device's
   native gates and connectivity and checks the compiled program against the
   circuit before sending it. Angles cross the wire exactly (§4).
4. **Returned, not recomputed.** A provider's counts are sealed as the device
   it names returned them (§8): `measured` for a processor, `simulated` for a
   simulator or an emulator, which no device measured. The result says where
   the counts come from, since counts rebuilt from probabilities are derived
   and not shot records, and what was set off at the provider. Nothing
   recomputes them.

## 2. Transports

Every message is a JSON object, and so is every answer. A client sends one
message and reads its answer before it sends the next on the same transport.

### 2.1 HTTP

A message is the body of a `POST` to the provider's endpoint (one URL for
every operation), with `Content-Type: application/json`. The answer is the
body of a `200` response, also for an answer with `"ok": false` (§3). Any
other status is a transport failure, not an answer.

A credential, when there is one, goes in `Authorization: Bearer <credential>`.
A client MUST NOT send a credential over `http://` to any host but this
machine (`localhost`, a loopback address); over the network it uses
`https://`.

### 2.2 A local process

The provider is a program the person names, with its arguments, started by
the client without a shell. Each message is one line of JSON on the program's
standard input; each answer is one line of JSON on its standard output, in
order. The program MUST write nothing else to its standard output. Its
standard error is its own log, and a client MAY discard it. The program runs
until its standard input ends.

A credential, when there is one, is in the program's environment as
`WAI_PROVIDER_CREDENTIAL`, never on its command line. A client SHOULD NOT
pass the program another provider's credential.

## 3. Messages and errors

Every message carries:

| field | |
|---|---|
| `protocol` | `"wai.quantum.provider/1"` |
| `op` | `describe`, `estimate`, `submit`, `status`, `result` or `cancel` |
| `settings` | optional: the person's non-secret settings for this provider (a device name, a region), passed as they are |

and the operation's own fields (§5). Every answer carries `"ok": true` and
the operation's fields, or:

```json
{ "ok": false, "error": { "code": "not_found", "message": "no job j-17" } }
```

The `message` is for a person; it MUST NOT quote a credential. The codes:

| code | when |
|---|---|
| `invalid` | the message is malformed: a field missing or of the wrong type, a program that does not read (§4), zero shots, a message without `protocol` |
| `unsupported` | an operation the provider does not offer (`cancel`, `estimate`), or an `op` it does not know |
| `unsupported_version` | a `protocol` other than this one |
| `unauthorized` | no credential, or one the provider does not accept |
| `not_found` | a job the provider does not hold |
| `refused` | a well-formed program the device cannot run: a gate outside its basis, a qubit it does not have, a pair it does not connect, too many shots; or a result asked of a job that has not finished or was cancelled |
| `quota` | the person's account cannot pay for or schedule the job |
| `unavailable` | the device is offline or in maintenance |
| `internal` | anything else |

## 4. The program

`estimate` and `submit` carry a `program` in the device's own gates, on its
physical qubits:

```json
{
  "format": "wai.native/1",
  "basis": "rzsxxcz",
  "bits": 2,
  "gates": [
    { "gate": "rz", "qubits": [0], "turns": [[1, 2]] },
    { "gate": "sx", "qubits": [0] },
    { "gate": "cz", "qubits": [0, 1] }
  ],
  "measure": [{ "qubit": 0, "bit": 0 }, { "qubit": 1, "bit": 1 }],
  "qasm3": "OPENQASM 3.0; …"
}
```

- `basis` is one of three gate sets, and every gate is of it:

  | basis | gates |
  |---|---|
  | `rzsxxcz` | `rz(t)`, `sx`, `x`, `cz` |
  | `prxcz` | `prx(t, p)`, `cz` |
  | `gpizz` | `gpi(p)`, `gpi2(p)`, `zz(t)` |

- An angle is a fraction of a full turn, `[m, k]` meaning `m/2^k` of 2π, with
  `0 ≤ m < 2^k` and `k ≤ 32`, so every angle crosses the wire exactly. The
  gates, as operators on the named qubits, up to a global phase:
  - `rz(t)` = exp(−iπt·Z); `sx` = √X; `x` = X; `cz` = diag(1, 1, 1, −1);
  - `prx(t, p)` = Rz(2πp)·Rx(2πt)·Rz(−2πp);
  - `gpi(p)` = Rz(2πp)·X·Rz(−2πp); `gpi2(p)` = Rz(2πp)·Rx(π/2)·Rz(−2πp);
  - `zz(t)` = exp(−iπt·Z⊗Z).
- `measure` gives, for each classical bit `0 … bits − 1`, the physical qubit it
  reads at the end of the program. Every bit reads exactly one qubit, and no
  qubit is read twice.
- `qasm3` is the same program as OpenQASM 3 on physical qubits (`$p`), the
  `prxcz` and `gpizz` gates defined in it from the standard gates, for a
  provider that reads OpenQASM. It is informative: where the two differ, the
  `gates` are the program.

A provider MUST refuse (`invalid`) a program of another `format`, a gate
outside its `basis`, an angle out of range, or a bit that reads no qubit; and
(`refused`) a program in a basis its device does not run, or on qubits or
pairs it does not have.

## 5. Operations

### 5.1 `describe`

No fields. The answer describes the device:

| field | |
|---|---|
| `protocol` | `"wai.quantum.provider/1"` |
| `provider`, `device` | the provider's name for itself, and the device's |
| `provenance` | `silicon` (a processor), `emulator`, or `simulator`; with `simulator` (or `emulator`) a sentence naming what it is, in the member of that name (`"simulator": "…"`) |
| `qubits` | the device's qubit count |
| `native_gates` | the gates it runs; a client compiles to the first of the bases of §4 whose every gate is listed |
| `connectivity` | `"all"`, or the pairs `[a, b]` a two-qubit gate may act on, in either order |
| `durations` | optional: `{"unit", "gates", "measure"}`: the unit (`ns`, `us`, `ms` or `s`), each native gate's duration wherever it runs (`{"sx": 35, "cz": 68}`), and the measurement's |
| `calibration` | `{"id"}`, the calibration snapshot the device is under; optionally `qubits` (`{"qubit", "readout_error", "error_1q", "t1_us", "t2_us"}`) and `edges` (`{"qubits": [a, b], "error"}`), each error from 0 to 1 |
| `billing` | `{"free": true}`, or `{"unit", "price", "currency"}`: the billing unit (`shot`, `job`, `second`, …), the price of one as a decimal string (`"0.0003"`), and the currency or credit it is in; optionally `free_tier`, the free use offered and its limits, as the provider states them |
| `max_shots` | optional: the most shots a job may ask |
| `seeded` | optional: `true` when a job's `seed` makes its counts reproducible (a simulator) |
| `approximate` | optional, with `simulator` or `emulator`: `true` when its results are approximate (a truncated tensor network, a sampled Pauli path); a result is then labelled so (§8.1) |

A client MUST NOT take a device to be free unless `billing` says
`"free": true`, or an estimate says so (§5.2).

A client MAY read a `describe` answer whose `calibration` states errors as a
device target (`wai.quantum.target/1`, `wai-quantum/src/engine/targets`)
and place the program on the qubits and pairs most likely to succeed. The
program it sends is checked against the circuit as every program is (§1), and
the receipt names how it was placed (§8).

### 5.2 `estimate`

Fields: `program` (§4), `shots`. The answer, before anything is submitted:

| field | |
|---|---|
| `free` | whether this job is free |
| `amount`, `currency` | when it is not: the cost, the amount as a decimal string |
| `unit` | optional: the billing unit the cost counts |
| `seconds` | optional: how long it is expected to take, queue included |
| `note` | optional |

A provider that cannot estimate answers `unsupported`; the client then treats
the cost as unknown (§7).

### 5.3 `submit`

Fields: `program`, `shots`, and optionally `seed` (a seeded simulator uses
it; a device ignores it). The answer's `job` is the job's id.

### 5.4 `status`

Field: `job`. The answer's `status` is `queued`, `running`, `done`, `failed`
or `cancelled`, with an optional `reason`.

### 5.5 `result`

Field: `job`. The answer, once the job is `done`:

| field | |
|---|---|
| `counts` | an object from outcomes to counts (§6), summing to the shots |
| `shots` | the shots |
| `calibration` | optional: the calibration snapshot the job ran under, when it is not the one `describe` names |
| `charged` | optional: `{"amount", "currency"}`, what the job cost |
| `energy` | optional: `{"kwh"}`, the provider's own figure for the job's energy |
| `counts_source` | optional: where the counts come from (§8.3): `shots` (tallied from one record a shot), `vendor_counts` (the device's or service's own counts), or `probabilities` (rebuilt from probabilities); an answer without it is read as `vendor_counts` |
| `execution` | optional: how the device ran the program (§8.3): each of `mitigation`, `twirling`, `decoupling`, `remapping` and `optimisation` as `off`, `on`, `none` (the device has no such step) or `unknown` (a step the provider cannot vouch for), `verbatim` (`true` when the gates ran as written, on the qubits named), and an optional `note` |

A `result` asked twice of the same job MUST give the same counts.

A provider that relays a job to another service (a wrapper around a service's
own interface) MUST give `execution`, from what it sets in its own request, and
MUST give `counts_source`, `probabilities` when it rebuilds counts from them.
Each is the provider's word, and sealed as such (§8.3). A client refuses a
`counts_source` or an `execution` not of the form above, and seals nothing.

### 5.6 `cancel`

Field: `job`. The answer's `status` is `cancelled`, or `done` for a job that
had already finished. A provider that cannot cancel answers `unsupported`.

### 5.7 A job outlasts its client

A device's queue can outlast any process waiting on it, so a job is not tied to
the connection or the process that submitted it. A provider SHOULD answer
`status` and `result` for a job's id from any later connection, and SHOULD keep
a finished job's result for as long as it can, and answers `not_found` for one
it no longer holds. A provider that runs as a local process (§2.2) ends with its
client unless it keeps its jobs itself.

A client:

- SHOULD record a job before it submits it, and the job's id as soon as it has
  one, with the program it sent, since a device's calibration can change before
  the counts are read;
- MUST NOT cancel a job only because it stopped waiting for it, unless it keeps
  no record to come back to;
- MUST NOT submit a job again when its `submit` did not complete, without the
  person's confirmation: the first may have been taken, and may be billed.

## 6. Counts and bit order

An outcome is a string of `bits` characters, `0` or `1`, the highest bit
first: its last character is bit 0. `"01"` is bit 0 read as 1 and bit 1 read
as 0. This is OpenQASM's order, and the job service's.

The client compiles each logical qubit `i` of the person's circuit to end on
the physical qubit that bit `i` measures, and reads the counts back onto the
program's own classical bits. The first job on a new provider SHOULD be the
probe (an `X` on qubit 0, every qubit measured), whose counts must all land
on `…01`; the conformance vectors begin with it (§9).

## 7. Cost and confirmation

A job is free when the provider's estimate says `"free": true`, or, for a
provider that gives no estimate, when its `billing` says so. Every other job,
an unknown cost included, is not free. A client:

- MUST show the estimate (or that the cost is unknown) before submitting;
- MUST NOT submit a job that is not free without the person's explicit
  confirmation for that run: a command-line flag (`wai-quantum run --provider
  NAME --yes`), an agent tool's argument the agent sets only after the person
  agreed (`run_on_provider` with `confirm_cost: true`), or a dialog (the
  desktop app);
- MUST NOT hold a standing confirmation across runs.

A tool that can submit a job that is not free MUST say in its description
that it can spend the person's money.

## 8. The receipt

The host seals each provider job as the job service seals its own, and the
same verifier checks both: an Ed25519 signature, by the host's key, over the
request as submitted and the result as returned (§8.2). A job's body is an
object holding `request`, the job as it was asked, and `result`, which carries
its `receipt`. This section says what a provider's result carries (§8.1), how
it is sealed and read (§8.2), what its counts' source and its `execution` say
(§8.3), what a verifier checks and the reason it gives for each failure
(§8.4), and what the receipt proves (§8.5). A verifier of §8.2 and §8.4 checks
any job's receipt, a provider's or not.

### 8.1 What a provider's result carries

| member | |
|---|---|
| `method` | `provider` |
| `accuracy` | `class`: `measured` when the provider's `provenance` is `silicon`, `simulated` when it is a simulator's or an emulator's (no device measured those counts); `approximate`: `true`, only on a `simulated` result whose provider's `describe` says `approximate` (§5.1), and absent otherwise; `note`, a sentence |
| `provider` | `name` (the person's name for it, the request's `provider`), `kind` (`sampler-v2`, `prx-circuits`, `native-turns`, `cloud-tasks`, `http` or `process`), `protocol` (`wai.quantum.provider/1`, or a built-in client's own), `device`, `provenance` (`silicon`; or `simulator` or `emulator`, alone or followed by `:` and the sentence naming it), `qubits` (the device's), `basis` (§4), and `describe`, the BLAKE3 of the RFC 8785 text (§8.2) of the provider's `describe` answer (for a built-in kind, of the description its client makes of the device). `provenance` is `silicon` exactly, or its part before any `:` is `simulator` or `emulator` |
| `calibration` | the calibration snapshot the job ran under, as the provider names it, or `null` |
| `compiled` | the program sent: `basis`; `qasm3`, its OpenQASM 3 text, `$i` being index `i` of the coupling map the provider described; `qubit_names`, the device's own name for each index the program uses, where a client sends it under those names (a `cloud-tasks` processor's labels, each index itself on its simulator; a `prx-circuits` device's qubits), each as it was when the job was sent; `program`, the BLAKE3 of `qasm3`'s UTF-8 bytes; `hash`, a sentence saying so; `native_gates`, `two_qubit_gates` and `swaps`, its counts; `layout` and `final_layout`, for each logical qubit the index it starts and ends on; `checked`, the check it passed before it was sent; `placement`, how it was placed (with a calibration read as a target, the target's hash, the layout's expected success and the fewest-swaps layout's) |
| `job` | `id`, the provider's id for the job, and `polls`, the status checks before it finished |
| `counts` | keyed by the program's classical bits (§6), each outcome that occurred with its count, at least 1 (a host drops an outcome a provider counts 0); with `distinct_outcomes`, the outcomes that occurred, and `omitted_shots`, the shots of outcomes not listed (at most 65,536 outcomes are listed). Every provider result an engine has sealed carries these, the earlier form's included |
| `shots` | the shots: the request's, 1024 when it gives none |
| `counts_source` | where the counts come from (§8.3) |
| `execution` | how the device was asked to run the program (§8.3) |
| `ideal` | the circuit's exact distribution p set beside the counts' frequencies q (up to 22 qubits): `computed`, and when it is `true` `total_variation_distance` (½ Σ \|q − p\|), `hellinger_fidelity` ((Σ √(p·q))²), `sampling_tvd_expected` (½ Σ √(2p(1 − p)/(π·shots)), the distance the shots alone leave on average, by the normal approximation) and `ideal_outcomes`; when `false`, `why`. The host computes them, and no verifier can without the program's simulation |
| `cost` | `free`; `estimate`, the estimate shown before submitting (an object, with `unknown`, a sentence, when none was known); `charged`, what the provider says it charged (`{"amount", "currency"}`), or `null`; `confirmed`, `true` exactly when the run was not free: such a run ran only with the person's confirmation (§7), and a free run needs none and records none |
| `energy` | `unmetered`, saying why it is not metered here; and, when the provider gives one, its own figure `service_kwh`, labelled as its own by `service_kwh_is` |
| `engine`, `schema` | the engine that made the result (`{"name": "wai-quantum", "version"}`) and `wai.quantum.job-result/1` |

A result sealed before results carried `counts_source` and `execution` is of
the *earlier form*: it carries neither, its class is `measured` whatever ran
it, and some of the members above are missing. It verifies by §8.4's rules
for that form. A verifier reads its class by its provenance, and says so: a
simulator's counts sealed as `measured` are a simulator's.

A job's `id` is the provider's, sealed: some name the person's account (a
cloud account's number inside a task's identifier). Sharing a receipt shares
it.

### 8.2 How it is sealed, and how a job is read

**The seal.** The result's `receipt` is an object:

| member | |
|---|---|
| `alg` | `ed25519` |
| `canon` | `rfc8785`; absent on a receipt of the first form (below) |
| `key` | the signer's Ed25519 public key `A`, its 32 bytes as 64 hex digits |
| `digest` | the digest signed, 32 bytes as 64 hex digits |
| `signature` | the signature `R ‖ S`, 64 bytes as 128 hex digits |

Hex digits are lower case, `0`–`9` and `a`–`f`, and nothing else. The receipt
is not sealed, so each member has one spelling. Any other member of the
receipt is ignored.

The digest is BLAKE3 (32 bytes, the hash mode) of

    d ‖ u64(|Q|) ‖ Q ‖ u64(|T|) ‖ T

where `d` is the 27 ASCII bytes `wai:quantum-service-receipt` and then the one
byte `0x02` (`0x01` for the first form); `Q` is the canonical text of `request`;
`T` is the canonical text of `result` with its `receipt` member removed; and
`u64(n)` is `n` as 8 bytes, big-endian. The signature is Ed25519 as RFC 8032
§5.1 defines it, with no context and not prehashed, over the 32 bytes of the
digest. Since the domain byte is inside the digest, a receipt of one form does
not verify as the other.

Only `request` and `result` are sealed. Whatever else a job's body holds (its
`status`, `id`, `method`, `why`, `estimate` and times) is the host's, and
unsealed: a verifier takes nothing from it, and its verdict does not change
with it.

**Reading.** A job is read from its text, strictly, before anything is
checked. The text is UTF-8 with no byte order mark, and one JSON value
(RFC 8259) with whitespace allowed around it. It is unreadable when:

- an object names a member twice (names compared after their escapes are read);
- a string holds a lone surrogate: a `\u` escape of a high surrogate not
  followed at once by one of a low surrogate, or of a low surrogate alone;
- a string holds a control character (U+0000 to U+001F) not escaped;
- a number's magnitude is 2^1024 − 2^970 or more, which rounds to an infinite
  double;
- arrays and objects nest deeper than 127, the outermost counted as the first.

A noncharacter (U+FDD0 to U+FDEF, and the last two code points of each plane)
is a character like any other. Each number is read as the nearest IEEE 754
double, ties to even; a number too small for any double other than zero reads
as zero. A whole number written as one (a literal with no fraction and no
exponent) in the request or the result that no double holds exactly says more
than the receipt seals, since the seal holds its double (§8.4,
`inexact_number`), unless it is the canonical text (below) of the double it
reads as. That one spelling is RFC 8785's for a whole double whose magnitude is
2^53 or more and under 10^21 (a negative one with its `-`), its shortest digits
padded with zeros (2^60 as
1152921504606847000), and what any JSON writer that follows RFC 8785 or
ECMAScript writes: it reads as the double that was sealed, and no other
spelling of an inexact whole number is taken. A canonical receipt seals every number as its double; a
first-form receipt seals a whole number written as one in its writer's integer
range by its digits, and every other number by its double (below), and only
what a receipt seals as a double is held to this.

**The canonical text** of a value is RFC 8785's:

- `true`, `false` and `null` as they are;
- a string between `"`s, with `"` and `\` escaped as `\"` and `\\`; U+0008,
  U+0009, U+000A, U+000C and U+000D as `\b`, `\t`, `\n`, `\f` and `\r`; every
  other character below U+0020 as `\u00` and two lower-case hex digits; and
  every other character as its own UTF-8 bytes, unescaped (`/` and U+007F
  included) and not normalised;
- a number as ECMAScript's Number::toString writes the double it reads as
  (ECMA-262, 6.1.6.1.20): zero (also negative zero) as `0`; otherwise, with
  `s` the fewest decimal digits `d1 … dk` that read back as the double (of
  those, the nearest to it, a tie going to the even last digit) and `n` such
  that the value is 0.`s` × 10^n: if k ≤ n ≤ 21, the digits and n − k zeros;
  if 0 < n ≤ 21, the digits with a `.` after the first n; if −6 < n ≤ 0, `0.`,
  −n zeros, and the digits; otherwise `d1`, then `.` and `d2 … dk` when k > 1,
  then `e`, a sign (`+` or `-`), and |n − 1| in decimal; a negative number is
  the same with `-` before it;
- an array as `[`, its values' texts joined by `,`, and `]`;
- an object as `{`, its members joined by `,`, each the name's text, `:` and
  the value's text, in the order of their names compared as sequences of
  UTF-16 code units (unsigned), and `}`;
- with no whitespace anywhere outside strings.

So a result verifies after any JSON reader and writer has carried it, even
one that writes `1.0` as `1` or orders members otherwise.

**The first form.** The first receipts, which name no `canon`, sealed the text
their signer's JSON writer wrote, not the canonical text, under the domain byte
`0x01`. A verifier reads `Q` and `T` as that writer did: the value's compact
text (no whitespace outside strings), its object members in the order the job
gives them (the result's `receipt` removed, the others kept in order), its
strings as in the canonical text, and its numbers so:

- a whole number written as one (no fraction, no exponent), without a `-` and
  from 0 to 2^64 − 1, or with a `-` and from −2^63 to −1, is written as its
  decimal digits, with its `-` (so `-0` is a double: `-0.0`);
- every other number is written from its double: `-0.0` for negative zero, and
  `0.0` for zero; otherwise, with `s` = `d1 … dk` and `n` as above, if n ≥ k
  and n ≤ 16, the digits, n − k zeros and `.0`; if 0 < n ≤ 16, the digits with
  a `.` after the first n; if −5 < n ≤ 0, `0.`, −n zeros and the digits;
  otherwise `d1`, then `.` and `d2 … dk` when k > 1, then `e`, a sign (`+`
  or `-`), and |n − 1| in decimal (`1e+16`, `1e-7`); a negative number is the
  same with `-` before it.

The first form was sealed from 2026-10-17 to 2026-10-20, every receipt by one
writer, whose text these rules restate.

A first-form receipt verifies only as its signer wrote it: reordered members
break it, and so can a number written another way. No host writes the first
form now. A `canon` other than `rfc8785` (`null` among them) names no form.

**The signature, strictly.** RFC 8032 leaves some choices open, and two
verifiers that chose differently would disagree on the same receipt. A
verifier here:

- decodes `A` and `R` as RFC 8032 §5.1.3 does, and refuses an encoding whose y
  (its low 255 bits, little-endian) is p = 2^255 − 19 or more, one that is not
  on the curve, and one with x = 0 and the sign bit set;
- refuses `A` of small order (a point P with [8]P the identity) and `A` not in
  the prime-order subgroup ([ℓ]A not the identity, ℓ = 2^252 +
  27742317777372353535851937790883648493);
- refuses `R` of small order, and `S` (little-endian) of ℓ or more;
- accepts exactly when [S]B = R + [k]A, with k = SHA-512(R ‖ A ‖ the 32
  digest bytes) read little-endian, mod ℓ, compared as points with no cofactor
  (and so as their canonical encodings).

Without the rules on `A`, the identity as `A`, any `S` and `R` = [S]B hold
for every digest: a receipt for any content at all. A library's default check
also takes the identity as `R` with `S` = 0.

### 8.3 Where the counts come from, and how the device was asked to run them

**`counts_source`** is one of:

| value | the counts are |
|---|---|
| `shots` | tallied from the device's records, one a shot |
| `vendor_counts` | the provider's own counts, as it returned them |
| `probabilities` | rebuilt from the probabilities the device gave: each `p·shots` rounded down, then the shots left given one each to the largest remainders, a tie to the lower outcome. They are derived, and not shot records |

Who did so is read from `provider.kind`. On a built-in kind the host's client
did, and it is the host's word. On `http` and `process` the host received counts
and did neither, and it is the provider's word for how it made them (§5.5): the
rule for rebuilding them is the one the provider says it kept, which the host
cannot vouch for.

**`execution`** says what the host knows of the five steps a provider may
take between receiving a program and running it: `mitigation` (correcting
counts after the run, readout correction or debiasing), `twirling` (of gates
or of measurements), `decoupling` (dynamical decoupling of idle qubits),
`remapping` (moving the program to other qubits) and `optimisation`
(rewriting its gates). It is an object of:

| member | |
|---|---|
| `stated_by` | `client`, the host's client from the request it sends; `provider`, the provider in its `result` answer (§5.5); or `nobody` |
| the five steps | each `off` or `on` (by a setting the client sent, or as the provider states), `default` (said by a client: its request sets nothing for it, so the provider's default stands, whatever it is), `none` (said by a provider: its device has no such step), or `unknown` (when nobody states it, or a provider cannot vouch for the step) |
| `verbatim` | `true` when the request asks for the gates to run as written, on the qubits named (a verbatim box); `false` when it does not; `null` when nobody states it |
| `sent` | with `client` only: `fields`, the members of the request's body, each once, in increasing order of their UTF-8 bytes; and `settings`, an object of what the request sets: JSON Pointers (RFC 6901) into the body, each with the value it holds there, and names for settings the body holds otherwise (`verbatim_box`); and nothing else |
| `note` | optional: a sentence |

and nothing else. Its rules: with `stated_by` `client`, each step is `off`,
`on` or `default`, `verbatim` is `true` or `false`, and `sent` is given; with
`provider`, each step is `off`, `on`, `none` or `unknown`, `verbatim` is
`true` or `false`, and there is no `sent`; with `nobody`, each step is
`unknown`, `verbatim` is `null` and there is no `sent`. Each key of
`sent.settings` either begins with `/`, and its first reference token (up to
the next `/`, its `~1` and `~0` read as `/` and `~`) is one of `fields`, the
rest of it not read; or is a name, not empty, that does not begin with `/`. A program run as written is
neither remapped nor optimised, so with `verbatim` `true`, `remapping` and
`optimisation` are each `off` or `none`. A client that says a step is `off` or
`on` says by which setting: `settings` is then not empty.

**What each built-in kind records.** A client builds its `execution` from the
very body it sends, so that what is stated is what was sent, and its counts'
source from what the service answered. These are their rows. A later client
that sends something else adds a row; a row is never removed, so every
receipt keeps verifying. A kind of the open protocol (`http`, `process`)
records the provider's word (`provider`), or `nobody` when it says nothing. On
any kind, a job recorded before its client stated what it sent, and collected
since, is stated by `nobody`: nothing else is known of what was sent.

| kind | `counts_source` | `sent.fields` | `sent.settings` | steps | `verbatim` |
|---|---|---|---|---|---|
| `sampler-v2` | `shots` | `backend`, `params`, `program_id` | `/params/options/dynamical_decoupling/enable`: `false`, `/params/options/twirling/enable_gates`: `false`, `/params/options/twirling/enable_measure`: `false` | `decoupling` and `twirling` `off`; the other three `default` | `false` |
| `prx-circuits` | `vendor_counts` | `circuits`, `shots` | none: the client sends nothing else | all five `default` | `false` |
| `native-turns` | `probabilities`: the service answers probabilities, so its counts are always rebuilt | `backend`, `input`, `settings`, `shots`, `type`; and `noise`, on its simulator under a device's noise model | `/input/gateset`: `native`, `/settings/error_mitigation/debiasing`: `false`; and `/noise/model`: the model's name, with `noise` | `mitigation` `off`; the other four `default` | `false` |
| `cloud-tasks`, on a processor (`silicon`) | `shots`, from the task's rows; `probabilities`, when its result holds none | `action`, `clientToken`, `deviceArn`, `outputS3Bucket`, `outputS3KeyPrefix`, `shots` | `verbatim_box`: `true`, the program's gates in the service's verbatim box on physical qubits | `remapping` and `optimisation` `off`; the other three `default` | `true` |
| `cloud-tasks`, on a simulator | as on a processor | as on a processor | none | all five `default` | `false` |

A row is the `execution` its kind states, less its `note`: `stated_by` is
`client`, and every member is as the row gives it. A row holds for its
provenance: `cloud-tasks`' first row for `silicon`, its second for any other
(a simulator or an emulator); `native-turns`' row with `noise` for any
provenance but `silicon`, its model's name a string, not empty, and its row
without `noise` for any. `prx-circuits` sends only
`circuits` and `shots`, so it turns nothing off, and says so. No row says a
provider runs a program as sent where its client sets nothing that asks it to.

### 8.4 What a verifier checks

A verifier reads the job (§8.2) and reports every reason below that applies,
as a set: none for a valid receipt. It is valid exactly when there is none.

| reason | when |
|---|---|
| `unreadable` | the text breaks a rule of reading (§8.2). Reported alone |
| `not_a_job` | the value is not an object whose `request` and `result` are both objects. Reported alone |
| `no_receipt` | the result has no `receipt`. Reported alone |
| `receipt_form` | the receipt is not an object; its `alg` is not `ed25519`; it has a `canon` other than `rfc8785`; or its `key`, `digest` or `signature` is not lower-case hex of its length. When it is reported, the next three are not checked |
| `digest_mismatch` | the digest the receipt states is not the digest (§8.2) of this request and result |
| `bad_key` | `key` is refused by the strict rules (§8.2). When it is reported, the signature is not checked |
| `bad_signature` | the signature does not hold, strictly (§8.2), over the digest the receipt states (so a body changed after it was sealed gives `digest_mismatch` alone) |
| `inexact_number` | a whole number written as one, in what the receipt seals (the request, and the result less its receipt; for a set, the request and its head, §8.6) and sealed as a double (§8.2), is not a double, and is not the canonical text of the double it reads as. A receipt is of the first form when `receipt_form` is not reported and it has no `canon`; one that `receipt_form` refuses names no form, and its numbers are read as a canonical receipt's |
| `provider_class` | a provider's result whose `accuracy.class` is not the one its form gives: `measured` in the earlier form; in the stated form, `measured` for `silicon` and `simulated` for a provenance whose part before any `:` is `simulator` or `emulator` (none for any other); or whose `accuracy.approximate` is present and not `true`, or `true` in the earlier form or on a class other than `simulated`. Or a result of another method whose class is `measured` or `simulated` |
| `request_provider` | a provider's result whose `provider.name` is not a string equal to the request's `provider` |
| `request_shots` | a provider's result whose `shots` is not the request's (1024 when the request gives none, or `null`) |
| `counts` | a provider's result whose `counts` is not an object of outcomes, all of one length and each of `0` and `1` only, to whole numbers from 1 to 2^53 − 1; or whose `shots` (a whole number of at least 1), `omitted_shots` and `distinct_outcomes` (whole numbers of at least 0) are missing; or whose counts and `omitted_shots` do not sum to `shots`; or whose `distinct_outcomes` is not the number of outcomes listed when `omitted_shots` is 0; or, when it is not 0, lists other than 65,536 outcomes, or no more distinct outcomes than it lists |
| `provider_members` | a stated provider's result (below) without, or with a wrong type of: `provider`'s `name`, `kind`, `protocol` and `device` (each a string not empty), `provenance` (as `provider_class` reads it), `describe` (64 lower-case hex digits), `qubits` (a whole number of at least 1) and `basis` (equal to `compiled.basis`); `calibration` (a string or `null`); `compiled`'s `basis` (`rzsxxcz`, `prxcz` or `gpizz`), `program` (64 lower-case hex digits), `qasm3` (a string), `native_gates`, `two_qubit_gates` and `swaps` (whole numbers of at least 0), `layout` and `final_layout` (arrays of whole numbers of at least 0) and `checked` (a string); `compiled.qubit_names`, where it is given or required, is below; `job`'s `id` (a string not empty) and `polls` (a whole number of at least 0); `ideal.computed` (`true` or `false`); `cost`'s `free` and `confirmed` (each `true` or `false`), `estimate` (an object) and `charged` (`null`, or an object whose `amount` and `currency` are strings); `energy.unmetered` (a string), and `energy.service_kwh_is` (a string) when there is `energy.service_kwh`; `schema` (`wai.quantum.job-result/1`); `engine.name` (`wai-quantum`) and `engine.version` (a string) |
| `counts_source` | a stated provider's result whose `counts_source` is not one of §8.3's values, or, for a kind with rows, not one the rows that hold for its provenance give |
| `execution` | a stated provider's result whose `execution` breaks a rule of §8.3; or, for a kind with rows, is neither one of the rows that hold for its provenance nor stated by `nobody`; or, for `http` and `process`, is not stated by `provider` or `nobody` |
| `compiled_program` | a stated provider's result whose `compiled.program` is not the BLAKE3 of `compiled.qasm3`'s UTF-8 bytes, when `qasm3` is a string and `program` is 64 lower-case hex digits |
| `cost_confirmation` | a stated provider's result whose `cost.confirmed` is `true` on a free run or `false` on one that is not free (§7), when both are `true` or `false` |

A provider's result is one whose `method` is `provider`. It is *stated* when it
carries a member `counts_source` or a member `execution` (whatever their
values), and of the earlier form when it carries neither; the earlier form is
held to `provider_class`, `request_provider`, `request_shots` and `counts`
only. A whole number is any number whose value is whole, however it is
written (`1`, `1.0`, `1e0`), however large; only a count's value has a ceiling,
and a sum is exact, not a double's. A number's value, in every rule here and on
every form of receipt, is its nearest double (§8.2): the digits of a first-form
receipt are for what it sealed, and for nothing checked here. `-0` is 0.
"The request's" is by value: two equal strings are the same, and that a string
is no number is another rule's to say.
A class a verifier does not know is not a reason: a later version may add one
(VERSIONING.md).

`compiled.qubit_names` is required of a stated result of `cloud-tasks` or
`prx-circuits` whose `execution` is stated by `client`, and optional otherwise.
When given (`null` is given, and is no object) it is an object: each key an index
in decimal, with no leading zero; each value a string, not empty; the value of
every entry of `layout` and `final_layout`, written in decimal in full, is among
its keys; and so is every index `qasm3` acts on, when `qasm3` is a string: each
`$` anywhere in it followed by one or more of the ASCII digits 0 to 9 names the
index those digits read as, written in decimal with no leading zero (a `$`
followed by no such digit names none). Otherwise it is a `provider_members` reason.

**What a host seals, its own verifier accepts.** A host refuses a request holding
a whole number past ±(2^53 − 1), or any number that it or RFC 8785 would write in
a spelling this section refuses, where it reads it, on every path to a seal (a
set's own request, its sweep's values among them, included); and it writes a
whole number no double holds in a result as the double it seals. A receipt an
earlier release sealed with such a number is refused with `inexact_number`,
though its signature holds: what it sealed is the double, and its text says
more. Written as that double's exact digits, or as its canonical text, it
verifies. The local hosts' `qec` took a seed of any size; their compile, pulse,
estimate and `@Seal` took any number in a request; and a set took any number in
its own request and its sweep's values. The job service refused such a number
in every request but a set's own.

A verifier also states, for a provider's result (one with no receipt
included), its class as its provenance reads it, and, in the stated form, its
counts' source and its `execution`, each as the result gives it.

These are all the rules: what §8.1 names and this table does not check
(`compiled.hash` and `placement`, the notes, the `ideal` figures, the
estimate's contents) is the host's word, unchecked. The schemas
(`wai.quantum.job-result/1`, `wai.quantum.receipt/2`) describe what a host
writes; a verifier judges by this section.

### 8.5 What the receipt proves, and what it does not

A valid receipt proves that the holder of the key it names signed this
request and this result, nothing else and nothing less: every member of
both, the counts, `counts_source` and `execution` among them. It does not
say whose key it is. The job service publishes its keys and their windows
(`GET /.well-known/wai-quantum-receipt-keys`); a host's key is the person's
own, and anyone relying on a receipt decides whether they trust it. Nothing
sealed carries a time: a receipt can be shown again, and no window can be
applied to it from its bytes. For the same reason a host can still seal a
result of the earlier form, stating neither its counts' source nor its
`execution`; a verifier then holds it to the earlier form's rules, reads its
class by its provenance, and says that it states neither. A host can likewise
seal a stated result with `execution` stated by `nobody` on any kind, meant for
a job recorded before its client said what it sent: that says nothing of what
was sent, and a verifier says that nobody stated it.

Everything the result says the host received, sent and checked is the host's
own word, signed: that this was the provider's answer, under the calibration
snapshot named, and that the host compiled the program whose text and hash it
carries exactly and checked it against the circuit before sending it. The
compiled program's text is in the receipt, with the device's names for its
qubits where its client uses them, so anyone can read which gates were sent to
which qubits and check its hash. It is not the bytes sent: a client writes the
same gates in what its service takes. `execution` says what the request
asked of the provider. What it does not prove: that the device ran that
program, or ran anything. The counts are the provider's word, and they are
not reproducible by recomputation: the same job run again gives other counts.
A provider's simulator's counts say what its software gives, which is why
they are `simulated`, not `measured`. The `ideal` distance says how far the
counts are from what the circuit gives; a distance well above the sampling
distance is the device's or the simulator's error, or a program not run as
sent.

### 8.6 A set's receipt

A set is one request that runs many jobs (`programs`, a list of programs or
jobs, or `sweep`, values for the inputs of the request's own `qasm`); its result
has `"format": "wai.quantum.set/1"`. Its `head` is its result less `items` and
`receipt`. The set's receipt seals the set's request and its head, by §8.2, as a
job's seals its request and result; each item that ran carries a result with a
receipt of its own, over its own request. A verifier reads a set as it reads a
job (§8.2), and reports, beside §8.4's reasons for the set's request and head
(`unreadable`, `not_a_job`, `no_receipt`, `receipt_form`, `digest_mismatch`,
`bad_key`, `bad_signature`, `inexact_number`; the provider rules do not apply to
a head), each of these that applies:

| reason | when |
|---|---|
| `set_items` | `items` is not a non-empty array whose `i`-th entry is an object with `index` equal to `i` (a whole number), `request` an object, exactly one of the members `result` (an object) and `error` (a string) present (a member that is present is so whatever its value, `null` included), and `path` an array of strings each of 64 lower-case hex digits. When it is reported so, the next two are not checked. Or: `count` is not the number of items; or the items are not the ones the request gives. With `programs` (an array, and no `sweep`), item `i`'s request is the request less `programs`, `sweep` and `dry_run`, with program `i` over it: a string as `qasm`, or an object's members, each replacing one of the same name. With `sweep` (an object, and no `programs`), each of its values is a non-empty array, the items are as many as the product of their lengths, and each item's request less `qasm` is the request less `programs`, `sweep`, `dry_run` and `qasm`, its `qasm` a string (the request's program written out at its point, which §8 does not check: below). A request with neither, or with both, gives no items; a member that is `null` is none. Requests are compared as JSON values: members in any order, numbers by value |
| `set_item` | an item with a `result` is not a valid job: `{"request": its request, "result": its result}`, read as §8.2 reads a job, gives any reason of §8.4; or its result is a set's (`format` `wai.quantum.set/1`: an item is one job); or its receipt is valid and names a `key` other than the set's receipt's `key` member as it stands (one the set's receipt does not have included). Or an item with an `error` has a request that writes a whole number its leaf could not seal: as `inexact_number` reads one in a canonical receipt, whatever the set's form, since a leaf is the canonical form's digest |
| `set_root` | `root` is not 64 lower-case hex digits, or not the Merkle root of the items' leaves; or an item's `path` does not lead from its leaf to `root` |

An item's leaf is the digest (§8.2, the canonical form's) of its request and its
result less its `receipt`, or, for an item with an `error`, of its request and
`{"error": the error}`. The Merkle tree: a leaf's hash is BLAKE3 of
`moq-jwp:merkle-leaf` and the byte `0x01`, then the leaf; a node's is BLAKE3 of
`moq-jwp:merkle-node` and the byte `0x01`, then the left hash and the right; a
level of odd length pairs its last hash with itself; the root of one leaf is its
hash. A path lists the sibling at each level from the leaf up: with `n` items it
has as many entries as halvings (rounding up) take `n` to 1, and a path of any
other length does not lead to the root; at each level the item's position,
halved at each step, says whether the sibling is on the right (an even position)
or the left. This is the tree the WAI worlds' and provenance receipts use, its
domains theirs, kept so that every set already sealed verifies. Two things
follow from it. The root alone does not fix the number of items (a last leaf
paired with itself gives the root a level of one more would), and `count`,
sealed in the head, does; one item shown with its path and the head alone is
placed by `count`. And the root does not say whether an item failed: an item
with an `error` and one whose result is `{"error": …}` have one leaf, and only
the item's own receipt tells them apart.

An item is read for its `index`, `request`, `result`, `error` and `path`; what
else it holds (its `status`, `method`, `why`, a sweep's `point`) is the host's,
unsealed, as a job body's members beyond its request and result are, and a
member its own receipt holds beyond its five is ignored, as a job's is.

The engine also writes each item of a sweep out again at its point, and says
whether each `qasm` is that program (`derived`, beside the reasons); that is the
engine's check of the set, not a rule of its receipt, and a verifier that does
not write programs out does not make it.

## 9. Conformance

`wai-quantum-qpu/conformance/provider-v1.json` holds the vectors:

- **messages**, sent as they are: an unknown operation (`unsupported`),
  another version (`unsupported_version`), a message without its protocol
  (`invalid`), a job that does not exist (`not_found`), malformed programs
  (`invalid`);
- **programs**, each compiled to the device, estimated, run with seed 1, and
  checked by its counts: the probe, bits named out of order, a phase only
  interference shows, a phase kicked back by two quarter-turn controlled
  phases, a Bell pair, and a triangle of two-qubit gates that needs routing
  on a line. The thresholds leave room for a device's noise;
- **reference**: the counts the reference provider gives for each program at
  seed 1, so its determinism is held on every platform.

The checker (`wai-quantum providers test NAME`) also checks the `describe`
answer in full, that zero shots are `invalid`, and that a program in another
basis is refused. It runs the programs only when every estimate says free,
or the person allows the spend (`--yes`).

The reference provider (`wai-quantum providers reference --stdio`, or
`--http ADDR`) is this protocol around WAI's exact simulator. It is the
provider the tests run against over both transports, and the template to
copy: a provider for any device is the reference with its simulation replaced
by the device. It states its counts' source and its `execution` (§5.5).

**Receipts.** `quantum-conformance/receipts/` holds job receipts and what a
verifier must say of each (§8.4): receipts made by the command line on this
machine and by the job service, provider jobs of every built-in kind through
services that follow their contracts, the earlier form, the first (receipts the
first form's own writer sealed, and others `generate.py` made from §8.2's text),
and receipts sealed to break one rule each. A
receipt from a person's account names that account, and is not published: it is
checked with the differential's `--extra`, outside CI. A receipt once made is
kept, pinned by its digest. Two verifiers judge every one of them, and every
receipt made from a valid one by changing one member, numbers among them to
values past a double's range of whole numbers: `wai-quantum verify`, and
`quantum-conformance/tools/receipt_verify.py`, written from §8 alone in another
language and sharing no code with the engine. They must report the same reasons
for every receipt, and the manifest's for each in the corpus. Every other
surface that verifies is held to the manifest by its own tests, given each
receipt's text: the job service over HTTP and as a tool, the command line's tool
(each takes the job's text), the Python package (text, bytes or a path), the
desktop app and a browser tab. A surface given a job already parsed into objects
(a tool's `request` and `result`, a Python dict) cannot see what parsing loses:
a member named twice (`unreadable`), and a whole number past what its reader
keeps (`inexact_number`). It reports every other reason, and is held to the
manifest on every receipt but those. A tool's text is a JSON string, which holds
UTF-8 only, so text that is not UTF-8 reaches a verifier only as a file's bytes.

## 10. Versioning

`wai.quantum.provider/1` is this text. A later version is a new `protocol`
string; a provider that speaks only this one answers `unsupported_version` to
another. Fields marked optional may be added to answers without a new
version, and a client MUST ignore fields it does not know.

A result's receipt seals every member it holds, so a member a later text adds
to the result is in no receipt sealed before it, and every such receipt
verifies as it did (§8.4's earlier form). `counts_source`, `execution`,
`compiled.qasm3`, the class `simulated` and `accuracy.approximate` were added
so, as were the answers' `counts_source` and `execution` and `describe`'s
`approximate`.
