Skip to main content

The life of a request

ZeroSignal is an open network for paid AI inference. Independent operators run the models; your client seals each prompt to the single operator that will answer it, pays for that one request out of an on-chain escrow, and gets back a signed receipt it can check against the price it was quoted.

This page follows one request from end to end, because almost every property the network claims comes from the order these steps happen in rather than from any one of them alone. The pages under Concepts go deeper on each phase; this is the thread that connects them.

The cast

Three pairs of words do real work here, and each is easy to collapse by accident.

Operator and node. An operator is an on-chain identity — an owner address that gets paid, and a stake that backs it. Under that identity run one or more nodes, each a single endpoint with its own URL and signing key. Clients route to a node, not to the operator as a whole. See What is an operator.

Relay and target. These are per-request roles, not kinds of machine. Every node is both: it forwards other people's sealed traffic, and it answers requests of its own. What matters is that it is never both for the same request.

Client and payer. A client is whatever software does your side of the protocol — finding operators, reserving a price, opening escrow, sealing the prompt, checking the receipt, settling. There are two and they do the same job: the chat app speaks the protocol itself in your browser, and zs-proxy runs on your machine and presents an ordinary OpenAI API on localhost so an existing tool can sit in front of it knowing nothing about any of this. They share one payer — the Algorand account that funds the escrow — so you can run both. Below, "you" means whichever one you are running.

The shape of the whole thing

Six phases: discover a node, reserve a price, open escrow, send the sealed prompt, collect the receipt, settle on chain.

Nothing along that path is taken on trust. The price is signed before you pay it. The key your answer will be sealed with is named before any money moves. The money is escrowed before the prompt is sent. The amount charged is signed before either side can settle, and neither side can settle alone.

Read that as a dependency chain rather than a message log. An operator that wanted to overcharge you would have to have decided to, and signed a number saying so, before it knew what you were going to ask.

Discovery

Every node publishes a details document over plain HTTP: the models it serves, its prices, its protocol version, and a short-lived public key that requests should be sealed to. That key is signed by the node's long-term key and lives about twenty minutes — so a key recovered later cannot open anything said before it. The rotation machinery is covered in Encryption & keys.

The document is public and unauthenticated. That's intended; it's a catalog, and prices you can't read are prices you can't compare.

Reading it still goes through a relay. A client that probed nodes directly would hand its address to every operator it merely considered, on a background schedule. Finding candidates doesn't have that problem: the operator registry lives on chain, so a client enumerates it by reading the ledger, then borrows one of those nodes to relay its HTTP probes at another.

Reserve

A reserve describes the shape of the work and nothing else: a model, a token budget, whether the reply will stream. No prompt. Request and response are both sealed, because the request names your payer account and the response carries a transaction that names it again.

One round-trip returns three things, and they are spent in three different later phases.

  • A signed ticket — the operator's quote for exactly this shape of work: the rates, a ceiling on what it may charge, an expiry, and a commitment (a hash) to the key it will seal your answer with. Publish a hash of a secret in advance and you are stuck with the secret.
  • The response key, wrapped to you — encrypted so only your client can unwrap it. Because the operator committed to its hash in the ticket first, an answer sealed with any other key is provable misbehaviour rather than an argument about what was agreed.
  • A pre-signed escrow call — the operator signs the transaction that opens escrow but does not submit it. You complete the group and broadcast it, which is what lets the operator pay the network fees for a payment only you can make happen.

The ceiling on that ticket is a number the operator wrote. A valid signature over it proves the operator committed to the price; it does not prove the price is fair. So before opening escrow your client recomputes the ceiling from the ticket's own signed rates and bounds it against the card the operator advertised. A ticket that doesn't reconcile is refused and the next candidate is tried. How the ceiling is sized is covered in The payment flow; what you actually pay is in Pricing.

Escrow

Opening escrow is two transactions in one atomic group — both commit, or neither does. The first is your USDC transfer of exactly the ticket's ceiling. The second is the operator's pre-signed call, which writes a record of the ticket on chain and marks it open.

No ALGO moves. The small on-chain reserve that every stored record requires is drawn from a deposit you make once and reuse, and the operator funds the network fees on both this call and the settlement later. On the ordinary path you spend nothing but USDC.

The identifier of that second transaction becomes the request's identity. It ties the prompt you are about to send to the money you just escrowed, and afterwards neither can be swapped for the other's.

This is also the one step in the whole lifecycle that happens in public, and it names your account. That comes back at the end.

Serve

The request that leaves your device is one JSON object with one field in it: ciphertext. What a relay sees is a routing envelope around an opaque blob.

POST /v1/zs/relay
X-Zs-Relay-Target: <operator id> # on-chain ids — the relay
X-Zs-Relay-Target-Node: <node id> # resolves them itself,
X-Zs-Relay-Path: /v1/chat/completions # never a URL
X-Zs-Relay-Method: POST
Content-Type: application/vnd.zs+json

{ "ciphertext": "..." } # forwarded byte-for-byte

Everything the target needs in order to admit the request is inside that seal, not beside it:

sealed envelope — opens only with the target's short-lived key
├─ reply_to_public_key
├─ algorand_tx_id
├─ ticket_id
├─ admission_tag
└─ body — the OpenAI request, unchanged

bound into the encryption, but never transmitted:
algorand_tx_id · ticket_id · frame_index

That last line is the interesting part. Those values are folded into the encryption as data that must match for the ciphertext to open, but they are never sent — both ends already hold them. It is what makes a ciphertext sealed for one request impossible to splice onto another, and the frames of a stream impossible to reorder, without either side transmitting the values that make it true.

The admission tag closes the obvious attack. The transaction that opened escrow is public, so the ticket's identifier is public with it, and anyone watching the ledger could try to spend your ticket before you do. The tag is a keyed hash over the ticket, the transaction and the body, computed with the response key — which only you and the operator hold. The target checks it before it consumes anything:

  1. Look up the ticket, without consuming it.
  2. Verify the admission tag.
  3. Check the reply-to key against the one named at reserve.

— the ticket is consumed below this line —

  1. Consume the ticket.
  2. Verify the escrow transaction on chain.
  3. Check the model matches the one the ticket was priced for.
  4. Apply the node's own request defaults, then forward upstream.

Everything above the line is free to fail — a forged tag costs the attacker nothing and costs you nothing. Below it the ticket is spent and the operator has done real work, so a failure there produces a receipt for zero rather than silence.

info

This is where the protocol stops covering the prompt. If the operator serves from its own weights, the plaintext goes no further. If it serves through a hosted API, the plaintext continues to that provider. Confidential compute is the configuration that closes both.

The answer, and the receipt

There is no plaintext success path. A response to an admitted request is sealed or it is refused — and it is your client that refuses it, not the network.

A streamed answer arrives as sealed frames, and after the last one two more: a sealed transaction group ready for settlement, and a sealed receipt. Then the ordinary [DONE] line every OpenAI-compatible tool already knows how to stop on.

index arrives from the node what your client passes on
----- ------------------------------- --------------------------
0 event: zs --> data: {"choices":[...]}
1 event: zs --> data: {"choices":[...]}
- : keepalive (dropped)
2 event: zs --> data: {"choices":[...]}
- event: zs-settle-group consumed — never passed on
3 event: zs-receipt consumed — never passed on
- data: [DONE] --> data: [DONE]

- = not sealed under a frame index, so it doesn't advance the counter

The receipt is the ticket's counterpart. It carries what actually happened — real input and output counts, the amount charged, the timing — plus a hash of the answer, signed by the same key that signed the ticket. Your client checks three things: that the signature holds, that the amount is inside the ceiling you escrowed, and that the hash matches the answer it actually decrypted. All three passing is what makes the charge something you verified rather than something you were told.

What the encryption does not catch is a stream cut short. Dropping the trailing frames yields a well-formed, correctly sealed, incomplete answer; the completion signals in the response are what tell you it ended early, not the cryptography.

Settle

The operator claims an amount, you acknowledge it, and the escrow finalises only when both halves agree — normally as one atomic group submitted the moment your client has verified the receipt. Three payments leave escrow at once: the operator's, the protocol's fee, and the remainder back to you.

Neither side can finalise alone, and that cuts both ways. Acknowledging is how you cap what you pay. Silence is how you accept whatever was claimed.

The deadline — the ticket's expiry plus a grace window — is the whole design. Before it, settlement is a negotiation and either side can freeze the ticket by disagreeing. After it (the dashed paths) the ticket resolves without further consent, and anyone can push the transaction that does it, so neither side depends on the other staying online. Exactly one of those two outcomes is ever reachable, and which one depends only on whether the operator's claim was already standing when the clock ran out.

Freezing stops the money and preserves both claims. It does not resolve anything, because there is no on-chain arbiter: the protocol provides non-repudiable signed evidence from both sides and a state that nothing will move, but nobody to weigh that evidence. Until something off chain does, the escrowed USDC and the deposit slot behind the record stay locked. The payment flow covers both disagreement paths from the operator's side.

What each party knows

The routing exists for one property: the operator that reads your prompt never receives your address, and the relay that has your address cannot read a word.

Because every node plays both roles, one operator does over time accumulate a great many addresses and a great many prompts. What it never accumulates is a pair: the relay and the target of any one request are always different operators, enforced on their owner keys, so the two halves are never joined inside a single honest party. If no relay qualifies the request fails rather than falling back to a direct send — see Relays.

Where it is qualified

You are pseudonymous to the target, not anonymous. "Never learns who you are" is a claim about identity, not about pseudonymity. The target sees no name, no account and no IP address, but it does read your payer address out of the reserve and confirm it against the chain. That address is stable across requests unless you rotate it, so a target can accumulate prompts against it, and the fact that you paid that operator at that time is public. This is a deliberate trade: it is what buys escrow-backed refunds and a receipt you can dispute.

The ledger is a second path between the two halves. Those dashed arrows are the residual. Opening escrow is public, so a relay that logs which address it forwarded for, to which target, and when, can scan the chain for a matching open near that moment. If the target is lightly loaded and the timing is tight, that recovers the payer. Sealing every leg turns an exact read off the wire into a statistical correlation against public data — a real improvement, and not a proof of unlinkability.

Metadata is visible. The path, the method, the timing and the size of the ciphertext are all on the wire, and nothing hides that the protocol is in use.

Collusion defeats it. Joining the two views requires the relay and target to cooperate, or one party to be both. Owner-key diversity blocks that for a single owner, so the residual is a Sybil — one operator registered many times under distinct keys. Multi-hop relaying is the stated answer, and it is not here yet.

The one thing still trusted

Everything above concerns who learns that you asked. The operator answering still reads what you asked, because running a model means reading the prompt. That is inherent to inference and it is the single thing the design trusts an operator with.

Confidential compute is the specified way out. The node runs inside attested hardware, the key your prompt is sealed to is generated inside that hardware, and your client verifies the attestation before it seals anything. The part that makes it more than a promise is that the enclave measures the software it is running: an operator cannot attach a debugger, add prompt logging, or swap in a modified build, because each of those changes the measurement and a node whose attestation no longer matches the published build stops receiving sealed traffic. It is not an undertaking not to look — it is an inability to.

warning

Confidential mode is specified but not yet shipped. The discovery field, the evidence endpoint and the client-side verifier all exist, but real hardware quote generation is not implemented — the only wired mode is a development stub that production clients reject. Until that lands, routing to an operator's advertised posture is not the same as routing to verified hardware.

Want More?

  • The payment flow — the same five phases from the operator's side, including how a node sizes its ceiling and drives settlement.
  • Encryption & keys — the key hierarchy, rotation, and forward secrecy.
  • Relays — how a relay is chosen, and the rules that keep relay and target apart.
  • Privacy & security — the same privacy split without the protocol detail.
  • Confidential compute — attested hardware in depth.