How it works

Dendra in five minutes

One AI request, from your prompt to the payment: client, relay, miner, chain, jury, payment.

Each step says what happens and names the file or symbol that does it, so you can check it in the open-source code. The note above the steps maps the file names to the public repository.

From prompt to payment

Six steps. The drawing and the list follow the same order.

The path of a request: 1, the client opens the job and seals the prompt; 2, the relay carries it; 3, the miner answers with an open model and commits on chain; 4, the chain records the commit and holds the fee; 5, if the job is drawn, a jury checks the answer; 6, the miner is paid at finality, or the client is refunded. A job that is not drawn goes straight from the chain to the payment. 1 Client opens the job on chain, seals the prompt 2 Relay a mailbox for sealed requests and answers 3 Miner decrypts, runs an open model, seals the answer prompt answer prompt answer commit 4 Chain records the commit, holds the fee 5 Jury other miners judge a random sample 6 Payment released at finality, or the client refunded if drawn verdict not drawn: released at finality The path of a request: 1, the client opens the job and seals the prompt; 2, the relay carries it; 3, the miner answers with an open model and commits on chain; 4, the chain records the commit and holds the fee; 5, if the job is drawn, a jury checks the answer; 6, the miner is paid at finality, or the client is refunded. A job that is not drawn goes straight from the chain to the payment. 1 Client opens the job on chain, seals the prompt prompt 2 Relay a mailbox for sealed requests and answers the miner fetches it 3 Miner decrypts, runs an open model, seals the answer commit on chain 4 Chain records the commit, holds the fee if the job is drawn 5 Jury other miners judge a random sample verdict 6 Payment released at finality, or the client refunded
The answer takes the same road back, from the miner through the relay to the client. A job that is not drawn for audit goes from the chain straight to the payment.

Where the files are. In the public repository, services/gateway.py, services/client.py, services/relay.py and services/miner.py hold the files named below dendra_gateway.py, dendra_client.py, relay_server.py and miner_daemon.py. The other Python files keep their names under services/ (modea/miner.py is services/modea/miner.py), and the Go files are under chain/x/jobs/keeper.

  1. Client: opens the job and seals the prompt

    You send a prompt to the gateway, which serves the chat-completions API. The gateway acts as the client on chain: it opens a job and puts the fee in escrow, reads which miner the chain assigned to it, takes that miner's encryption key from the chain, and seals the prompt to that key.

    In the code: dendra_gateway.py::run_dendra, dendra_client.py::submit_job, and the chain's draw of the assigned miner, committee.go::deriveCommitteeOrdered.

  2. Relay: a mailbox for sealed messages

    The sealed prompt is deposited at the relay, under the job and the miner it is for. The relay keeps it on disk and holds no key that opens it. Later the sealed answer waits there for the client. A miner lists only the keys that end with its own id.

    In the code: the /req, /res and /list routes of relay_server.py, and relay_client.py::listing.

  3. Miner: decrypts, answers, commits

    The assigned miner finds the request, decrypts it in locked memory and runs an open model on its own hardware. It deposits the sealed answer at the relay, then anchors a commit on chain: a commitment to the question and an embedding of the answer, not their text.

    In the code: miner_daemon.py::main (its job loop), modea/miner.py::Miner.handle_job.

  4. Chain: records the commit, holds the fee

    The client sends one settle transaction. When verification_mode is 1 (optimistic), a single miner answers: the chain checks that its commit exists, holds back hold_bps of the miner's share until finality (in basis points, so 10000 holds all of it) and pays the rest at once. The protocol cut stays in the module until finality too, and so does the burn when hold_bps is above zero. When verification_mode is 0, a committee of miners answers each job (its size is work_committee_size) and the chain compares their answers instead.

    In the code: msg_server_commit.go::CreateCommit, msg_server_settle_semantic.go::SettleSemantic and settleOptimistic.

  5. Jury: a random sample is checked

    After settlement, the random seed of a later block decides whether the job is audited (audit_sample_bps). If it is, the chain seats a jury of other present miners, never the audited one. A juror that runs a judge opens the audited miner's sealed reveal, computes its own answer to the same prompt, compares the two and posts a verdict on chain. Only a miner started with its judge on posts verdicts, so a seat without a judge stays silent. A conviction needs three things together: enough jurors voting (audit_min_quorum), at least that many, or two thirds of the seats if that is larger, voting invalid, and a majority of the voting stake.

    In the code: audit_sampling.go::runOptimisticAudit, audit_committee.go::drawAuditCommittee, antievasion.go::auditRelativeBar, and the judge side in judge_worker.py.

    Planned, not described above. Consensus epoch 15 makes jurors declared judges: a miner would declare its judge on chain, and only declared judges would be drawn. This step describes the chain before that upgrade, where any registered, present miner can be drawn onto a jury. It changes when the upgrade is applied.

  6. Payment: released, or refunded

    Not drawn, or upheld by the jury: the held share is released at finality and the miner is paid. Convicted: the miner loses part of its bond (slash_leak_bps) and the client is refunded. Too few jurors, or no verdict: the chain waits. When audit_unwind_blocks is above zero and the held share is older than that many blocks, the held share is refunded to the client and nobody is slashed. At finality the protocol cut also goes to the validators, the team and the treasury (protocol_fee_bps, validator_reward_bps, team_fee_bps), and the burn is applied (fee_burn_bps).

    On top of the fee, the Final Testnet Season pays testnet DNDR once a week, by a published formula, for verified work, juror verdicts, proven presence (on a day the identity also served a verified request) and, from a day the season announces, an active judge.

    In the code: antievasion.go::releaseHeld, audit_unwind.go::unwindStrandedRetention, final_season_payout.py, final_season_rules.py.

Who can read the prompt

The gateway, the miner that answers and, when the job is audited, the jurors who receive the reveal. The relay and the chain cannot: the relay stores ciphertext, and the chain stores a commitment and an embedding. The roadmap lists what is planned to change.

Read the settings

The numbers behind these steps are parameters of the chain, so this page names them and never quotes them.

Parameters of the jobs moduleany terminal
$ curl -s https://testnet-api.dendranetwork.com/rest/dendra/jobs/v1/params

A field missing from that output holds zero. For some, audit_min_quorum among them, zero selects a default compiled into the chain, not "no floor". The fee split, the hold and the audit rate are all there. For the same steps with their diagrams, see After the answer in the docs.