Skip to main content

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

Mirrored from the canonical text at commit 260cbd09 ().

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"
opdescribe, estimate, submit, status, result or cancel
settingsoptional: 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:

{ "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:

codewhen
invalidthe message is malformed: a field missing or of the wrong type, a program that does not read (§4), zero shots, a message without protocol
unsupportedan operation the provider does not offer (cancel, estimate), or an op it does not know
unsupported_versiona protocol other than this one
unauthorizedno credential, or one the provider does not accept
not_founda job the provider does not hold
refuseda 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
quotathe person’s account cannot pay for or schedule the job
unavailablethe device is offline or in maintenance
internalanything else

4. The program

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

{
  "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; …"
}

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, devicethe provider’s name for itself, and the device’s
provenancesilicon (a processor), emulator, or simulator; with simulator (or emulator) a sentence naming what it is, in the member of that name ("simulator": "…")
qubitsthe device’s qubit count
native_gatesthe 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
durationsoptional: {"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_shotsoptional: the most shots a job may ask
seededoptional: true when a job’s seed makes its counts reproducible (a simulator)
approximateoptional, 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
freewhether this job is free
amount, currencywhen it is not: the cost, the amount as a decimal string
unitoptional: the billing unit the cost counts
secondsoptional: how long it is expected to take, queue included
noteoptional

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
countsan object from outcomes to counts (§6), summing to the shots
shotsthe shots
calibrationoptional: the calibration snapshot the job ran under, when it is not the one describe names
chargedoptional: {"amount", "currency"}, what the job cost
energyoptional: {"kwh"}, the provider’s own figure for the job’s energy
counts_sourceoptional: 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
executionoptional: 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:

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:

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
methodprovider
accuracyclass: 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
providername (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
calibrationthe calibration snapshot the job ran under, as the provider names it, or null
compiledthe 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)
jobid, the provider’s id for the job, and polls, the status checks before it finished
countskeyed 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
shotsthe shots: the request’s, 1024 when it gives none
counts_sourcewhere the counts come from (§8.3)
executionhow the device was asked to run the program (§8.3)
idealthe 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
costfree; 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
energyunmetered, 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, schemathe 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
alged25519
canonrfc8785; absent on a receipt of the first form (below)
keythe signer’s Ed25519 public key A, its 32 bytes as 64 hex digits
digestthe digest signed, 32 bytes as 64 hex digits
signaturethe 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:

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:

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:

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:

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:

valuethe counts are
shotstallied from the device’s records, one a shot
vendor_countsthe provider’s own counts, as it returned them
probabilitiesrebuilt 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_byclient, the host’s client from the request it sends; provider, the provider in its result answer (§5.5); or nobody
the five stepseach 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)
verbatimtrue 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
sentwith 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
noteoptional: 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.

kindcounts_sourcesent.fieldssent.settingsstepsverbatim
sampler-v2shotsbackend, params, program_id/params/options/dynamical_decoupling/enable: false, /params/options/twirling/enable_gates: false, /params/options/twirling/enable_measure: falsedecoupling and twirling off; the other three defaultfalse
prx-circuitsvendor_countscircuits, shotsnone: the client sends nothing elseall five defaultfalse
native-turnsprobabilities: the service answers probabilities, so its counts are always rebuiltbackend, 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 noisemitigation off; the other four defaultfalse
cloud-tasks, on a processor (silicon)shots, from the task’s rows; probabilities, when its result holds noneaction, clientToken, deviceArn, outputS3Bucket, outputS3KeyPrefix, shotsverbatim_box: true, the program’s gates in the service’s verbatim box on physical qubitsremapping and optimisation off; the other three defaulttrue
cloud-tasks, on a simulatoras on a processoras on a processornoneall five defaultfalse

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.

reasonwhen
unreadablethe text breaks a rule of reading (§8.2). Reported alone
not_a_jobthe value is not an object whose request and result are both objects. Reported alone
no_receiptthe result has no receipt. Reported alone
receipt_formthe 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_mismatchthe digest the receipt states is not the digest (§8.2) of this request and result
bad_keykey is refused by the strict rules (§8.2). When it is reported, the signature is not checked
bad_signaturethe 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_numbera 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_classa 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_providera provider’s result whose provider.name is not a string equal to the request’s provider
request_shotsa provider’s result whose shots is not the request’s (1024 when the request gives none, or null)
countsa 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_membersa 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_sourcea 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
executiona 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_programa 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_confirmationa 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:

reasonwhen
set_itemsitems 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_iteman 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_rootroot 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:

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.