Reference
Protocol reference
Constants, the decision request schema, Jev-compatible question and answer types, probability transport, signed envelopes, miner responses, receipts, admission snapshots and error classes. Field names are taken from the reference code and spec revision 0.6.
Pre-release wire format
These formats will change before any network launch. Where the reference code and spec 0.6 differ, this page shows what the code does today and notes the spec target. Values marked (provisional) live in signed policy and are not protocol facts.
Constants
| Constant | Value | Notes |
|---|---|---|
| Canonical form | RFC 8785 (JCS) | Signed field names ASCII only. Already used by the reference code; required from G1 |
| Signature | Ed25519 (RFC 8032) | Public keys validated as curve points on load. Serving keys are separate from Bittensor wallet keys |
| Hash | SHA-256, lowercase hex | |
| Clock skew allowance | 2 000 ms (provisional) | |
| Request lifetime | ≤ 60 000 ms | MAX_REQUEST_LIFETIME_MS in code |
| Request bytes | ≤ 256 KiB excluding images (provisional); state ≤ 64 KiB; each question text or criteria string ≤ 8 KiB | Spec target. Code today: miner wire body ≤ 1 MiB while images are disabled |
| Images | ≤ 4 per request; each ≤ 6 MiB decoded; PNG, JPEG or WebP with matching magic bytes; ≤ 4 096 px per side and ≤ 16 777 216 px | Decoded only in an isolated worker. Disabled at the network miner today. Code today: ≤ 4 000 000 px |
| Sequence numbers | Integers in [1, 253 − 1] | |
| Ledger epoch | epoch_tempos = 4 tempos (provisional) | Tempo read only from the chain-profile readback |
| Fidelity window | W = 6 ledger epochs | Strictly non-overlapping; one statistical look per window |
| Probability transport | JSON numbers read as IEEE-754 binary64 | Sum within 1e-6 of 1 is renormalized; anything else is rejected |
| Questions per request | ≤ 32 | MAX_QUESTIONS in code |
| Options or levels per question | 2 – 64 | MAX_OPTIONS in code |
Request schema
A decision request is a JSON object validated by DecisionRequest.from_payload. Unknown fields are rejected, not ignored. All values must be finite JSON that RFC 8785 can canonicalize.
| Field | Type | Rule |
|---|---|---|
protocol_version | string | Must be "1" |
request_id | string | ^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$ |
nonce | string | 32–128 URL-safe characters [A-Za-z0-9_-] |
tier | string | gdpr_eu or global_permissionless |
model | string | Public catalog alias, ^[a-z0-9][a-z0-9._-]{0,63}$ |
state | any JSON | The context the questions are about |
questions | object | 1–32 questions keyed by stable IDs (same pattern as request_id) |
images | array, optional | ≤ 4 data URLs data:image/(png|jpeg|webp);base64,…, each non-empty and ≤ 6 MiB decoded |
deadline_unix_ms | integer | In the future and at most 60 000 ms (plus the peer skew allowance of up to 2 000 ms) ahead |
An illustrative request with one question of each type:
{
"protocol_version": "1",
"request_id": "req-0001",
"nonce": "b3Vy-ZXhhbXBsZS1ub25jZS0zMi1jaGFycy1taW4",
"tier": "global_permissionless",
"model": "example-alias",
"state": { "ticket": "Order 1042 arrived damaged. The customer wants their money back." },
"questions": {
"wants_refund": { "type": "noul", "instructions": "Is the customer asking for a refund?" },
"queue": {
"type": "choice",
"instructions": "Which queue should handle this ticket?",
"criteria": { "billing": "Payments and refunds", "shipping": "Delivery problems", "other": "Anything else" }
},
"urgency": {
"type": "score",
"instructions": "How urgent is this ticket?",
"criteria": ["low", "medium", "high"]
}
},
"deadline_unix_ms": 1790000000000
}
The model alias is a placeholder; no public catalog is published yet. A real deadline_unix_ms must be within 60 seconds of the current time.
Question types
REAX keeps the Jev-compatible typed-decision shape. Each question object may contain only type, instructions (any JSON) and criteria.
| Type | Candidate set | criteria |
|---|---|---|
noul | true / false | Optional. If present, an object with exactly the keys true and false |
choice | One of N options | Required object with 2–64 entries keyed by option ID |
score | One of N ordered levels | Required array of 2–64 levels, lowest first |
Answers
A response must contain exactly one answer per question ID. Each answer must have exactly the fields below; any extra field (for example an explanation) fails validation.
| Type | Fields | Rules |
|---|---|---|
noul | type, noul | noul is a probability in [0, 1] |
choice | type, choice, confidence, probabilities | probabilities has exactly the declared option IDs; choice is the first maximum-probability option; confidence equals its probability |
score | type, score, confidence, probabilities, legend | probabilities keyed "0"…"N-1"; score equals the expected level Σ i·pi; confidence equals the largest probability; legend maps each index to its original criteria level |
Answers for the request above:
{
"wants_refund": { "type": "noul", "noul": 0.94 },
"queue": {
"type": "choice",
"choice": "billing",
"confidence": 0.81,
"probabilities": { "billing": 0.81, "shipping": 0.15, "other": 0.04 }
},
"urgency": {
"type": "score",
"score": 1.1,
"confidence": 0.5,
"probabilities": { "0": 0.2, "1": 0.5, "2": 0.3 },
"legend": { "0": "low", "1": "medium", "2": "high" }
}
}
Probability transport
- Every probability is a finite JSON number in [0, 1], read as IEEE-754 binary64. Booleans are not numbers.
- A distribution must cover exactly the declared outcomes and sum to 1 within
1e-6(PROBABILITY_TOLERANCE). Spec 0.6: the validator renormalizes such a vector and rejects any other; a vector summing to 1 ± 1e-5 is rejected. - Derived fields (
choice,confidence,score) must agree with the distribution within the same 1e-6 tolerance. - Probabilities must come from deterministic extraction (
label_logits_v1orseq_logprob_v1), never from parsing generated text. See the miner guide.
Envelopes
Signed request envelope (code)
Both the gateway ingress and the miner accept a reax.request.v2 envelope. The signature is Ed25519 over the RFC 8785 form of the envelope without its signature field, hex-encoded (128 characters). It must have exactly these fields:
{
"schema": "reax.request.v2",
"audience": "<receiver: gateway audience or miner hotkey>",
"caller": "<caller id>",
"request": { "…": "decision request, as above" },
"signature": "<128 hex chars>"
}
The caller must hold an unexpired grant for the request's tier. A grant fixes the caller's public key, tiers, expiry and exactly one data class; the data class lives only in the grant and never on the wire, so a customer request cannot be relabelled as synthetic.
Transport authorization header
Every HTTP call also carries Reax-Authorization: the unpadded base64url encoding of a signed reax.transport.v1 document, at most 2 048 bytes. It lets the receiver reject unknown callers before reading the body.
{
"schema": "reax.transport.v1",
"caller": "<caller id>",
"audience": "<receiver>",
"body_sha256": "<SHA-256 of the exact body bytes>",
"expires_at_ms": 0,
"signature": "<128 hex chars>"
}
Receivers accept an expiry no more than 62 s ahead; the reference clients emit at most 10 s and never beyond the request deadline.
Gateway dispatch
Before dispatch the gateway replaces the caller's request_id with a fresh d_-prefixed random ID and issues a fresh nonce, so customer-facing identifiers never reach a miner.
Spec 0.6 targets
- Request token
reax.request.v2signs{schema, caller_id, key_id, audience, tier, data_class, model, request_id, nonce, request_sha256, issued_at, expires_at}, whererequest_sha256is the JCS digest of the admitted payload. Grants come only from a signed caller registryreax.callers.v1and fix exactly one data class,syntheticorreal. - Projection. The gateway builds
miner_payloadfrom an explicit, versioned allow-list per request type (question type and text, candidate options in dispatch order, criteria,state, images, model alias) and drops everything else. A payload containingdata_class,synthetic,probeor an equivalent field is rejected, not stripped. - Gateway → miner envelope contains only schema, audience (miner hotkey, network, netuid), the gateway ingress caller ID, a fresh random
dispatch_id,miner_payload, nonce, deadline and the gateway signature. It must not contain a data class, a probe, organic or burst indicator, a customer or tenant identifier, or the System1 key.
Code vs spec
The code's data classes are synthetic and customer (spec: synthetic and real). The code forwards the full decision request, including its tier field, inside the miner envelope; spec 0.6 forwards only the allow-listed miner_payload and adds dispatch_id.
Miner response
Code today: reax.response.v1 with fields schema, miner, request_digest (SHA-256 of the canonical request envelope), nonce, result and signature. result contains request_id, model, answers, usage (input_tokens, output_tokens, decisions), latency_ms, tokenizer_version and model_revision. A full example is in the miner guide.
Spec 0.6: the signed body is reax.response.v2:
{
"schema": "reax.response.v2",
"miner_hotkey": "…",
"serving_key_id": "…",
"dispatch_id": "…",
"request_digest": "<miner_payload_sha256>",
"nonce": "…",
"manifest_sha256": "…",
"answers": { },
"usage": { "input_tokens": 0, "output_tokens": 0, "decisions": 0 },
"completed_at_ms": 0
}
The gateway must reject the response on any of: wrong signature or key; digest, dispatch ID or nonce mismatch; a manifest_sha256 that is not the admitted or registered manifest; decisions not equal to the number of questions; an answer field outside the per-type allow-list; a probability vector that fails transport rules; arrival after the deadline on the gateway's monotonic clock; or a revocation of the miner, operator, serving key, manifest, origin or subprocessor observed while the request was in flight. completed_at_ms is informational.
Receipts
Receipts are payload-free. They never contain the request, the answer, a customer identifier or a label.
| Code today (gateway ledger) | Spec 0.6 reax.receipt.v2 |
|---|---|
request_id (caller-scoped keyed ID), tier, miner_uid, model_revision, policy_digest, response_digest, measured_latency_ms, observed_at_ms, status, plus a ledger-keyed signature | Request ID, tier, data class, miner hotkey and UID, manifest, policy digest; request fingerprint, miner_payload_sha256, dispatch_id, response digest; usage, measured latency, outcome, delivery, billing-policy ID, canon; leaf_commitment on every receipt. Ed25519-signed with a key_id |
A receipt may say authenticated only after every check passes. It never says verified: signatures and digests do not prove model execution or answer correctness. Answers exist only in gateway memory until written to the caller; spec 0.6 target: a retry of a completed request returns 409 completed_not_retained with the receipt ID and no answer. The code today returns 409 request_conflict.
Admission snapshot
GDPR-tier miners are admitted through a short-lived snapshot signed by the admission issuer. Serving processes hold only the public verification keys, and every verifier persists a monotonic sequence floor so an older snapshot can never be re-accepted.
| Level | Code today (reax.admission.v2) | Spec 0.6 (reax.admission.v3) |
|---|---|---|
| Document | schema, key_id, sequence, issued_at, expires_at, entries, signature; validity ≤ 24 h | Adds network, genesis_hash, netuid, audiences, policy_epoch; validity ≤ 24 h (6 h recommended, provisional) |
| Entry | hotkey, country_code (EU member state), models, model_revisions, serving_public_key, and SHA-256 evidence references company_ref_sha256, dpa_sha256, kyb_sha256, location_attestation_sha256, identity_attestation_sha256 | hotkey, coldkey, operator_id, establishment_country, processing_countries, subprocessors (each with an authorization digest), endpoint_origin, tls_spki_sha256, serving_public_key, manifests, models, evidence |
Snapshots contain digests of independently checked evidence, never identity documents. Digests bind records; they do not make the records true. Spec 0.6 adds a durable, restrict-only revocation feed (reax.revocation.v1) whose entries never expire, a checkpoint that makes an emptied store fail closed, and routing that matches the requested region against every processing and subprocessor country.
Error classes
Reference code exceptions
| Exception | Raised when |
|---|---|
ProtocolError | A request or answer violates the versioned schema (fields, formats, limits, probabilities, deadline) |
AuthenticationError | Envelope, signature, audience, grant, tier or transport authorization fails, or a request or nonce was already claimed |
RouteDenied | Policy leaves no eligible miner with capacity (for example a disabled route or a missing admission snapshot) |
IntegrityFailure | A miner result does not match its approved identity, the question set, the answer rules or the deadline |
EngineProtocolError | The local engine or a remote miner cannot satisfy the contract |
AdmissionError | A signed admission snapshot is invalid, stale or rolled back |
ChainGuardError | A chain identity, runtime, freshness or commit-reveal guard fails |
HTTP status codes
| Status | Body error | Miner | Gateway |
|---|---|---|---|
400 | invalid_request | yes | yes |
403 | unauthorized_or_replayed | yes | yes |
404 | not_found | yes | yes |
409 | request_conflict | – | yes |
429 | capacity | yes | yes |
502 | inference_failed | yes | yes |
503 | route_unavailable | – | yes |
504 | deadline_exceeded | yes | yes |
Attempt outcomes
Validators record every scheduled attempt with one typed outcome. The first five are scored attempts (success counts for the miner, the other four against it); the rest are excluded because the miner is not at fault or the attempt is not a scored dispatch.
| Outcome | Availability |
|---|---|
success | Success |
timeout, unavailable, invalid, wrong_identity | Miner-attributable failure |
queue_expired_before_dispatch, capacity_burst_unscored, validator_fault, gateway_egress_fault, chain_rpc_fault, revoked_in_flight | Excluded |
Named conditions in spec 0.6
- revoked_in_flight
- A matching revocation was observed between dispatch and delivery: answer withheld, retryable
503, charge voided, not scored. - revocation_stale
- The revocation document is older than its freshness bound; GDPR dispatch fails closed.
- payload_binding_mismatch
- Dispatch and reference payload digests differ; observation discarded with no miner effect.
- completed_not_retained
409for a retry of a completed request; the answer is not stored anywhere (spec 0.6 target; code today returnsrequest_conflict).- chain_mismatch
- Live chain state differs from the signed chain profile; no transaction.
- chain_constraint
- The weight vector would violate a chain constraint; the validator abstains.
- reveal_missing / reveal_mismatch
- Post-reveal readback failed or differs by more than ±1 u16.