What the Oracle Is
The Demos Network Oracle (DNO) is a public observability layer for the Demos blockchain testnet. It reads the network's public seeds from outside, measures whether the heights they report agree, tracks specific problems, and publishes what it finds. When fewer than two seeds give their own height, it reads validators that answer as listed in their place, and says so.
The Oracle is strictly watch-only. It observes, interprets, and summarizes. It does not command validators, does not advise operators, and does not make predictions about what will happen next. It publishes what it observed and the rules it applied. What anyone does with that is their decision.
The Oracle is independent. It is not operated by the Demos core team. It does not control any downstream system. Any agent, builder, or person can consume its output and form their own conclusions.
DNO itself does not validate, vote or take part in consensus. Its operator, XM33, runs a validator on the public Demos testnet. In the public reading that validator has no special place: it counts like any other validator, and only if it publishes an address on chain and answers there as the listed key. DNO does not publish which validator it is.
What It Measures
Every reading is published as seven values. Each answers one question about what DNO observed. Some are built from others: status uses data quality, agreement, public incidents and how long DNO has counted without a new height; risk combines status, incidents, confidence, agreement, how many seeds answered and whether validators stood in for one.
| Concept | Question | Possible values |
|---|---|---|
| Status | What did the heights DNO read show this cycle? | stable · degraded · unstable · unknown |
| Trend | Is the condition changing? | improving · stable · worsening · unknown |
| Risk | Which observed conditions reduce the margin of this reading? | low · elevated · high |
| Data quality | Did DNO get at least two fresh heights to compare? | sufficient · insufficient |
| Confidence | Are the compared heights close, and is a seed's own height among them? | clear · uncertain |
| Agreement | Do the heights DNO compared align? | strong · moderate · weak · unknown |
| Incidents | Is a public incident, or one of DNO's own condition records, open? | counts; severity none · info · warning · critical |
Status
Status describes what DNO saw this cycle of the nodes its reading is made of: the public seeds, or, when fewer than two of them reported their own height, the validators standing in (see Witnesses). It says whether there is a reading, whether the heights agree, whether DNO has counted 30 minutes without a new height, and whether a public incident is active. It is not a measure of network size or validator count.
stable — There is a reading (at least two public seeds reported their own block height, or validators stood in for a missing one), the heights compared agree (strong agreement), no warning or critical public incident is active, and DNO has not counted 30 minutes without a new height. One seed not answering does not by itself change status. status_reason says "Heights advancing" only when DNO saw a new height arrive at a seed no more than two round lengths (40 s) before that observation: at the usual pace of one round every 20 s, in its own round or in one of the two before it. When the count described below reaches 5 minutes it says "no new height for N min"; beside one seed, where the count is the seed's, it says "the seed has shown no new height for N min". height_static_seconds carries that count in every reading. At a restart DNO replays the stored rounds of the last 24 hours by the same rule, so a standstill is kept across it; one that began earlier is then counted from the oldest round replayed. In the first round on a store with no rounds the count starts at 0.
The count, and a new height. height_static_seconds is how long DNO's count has run: the time without a new height, as DNO reads it. What DNO has read: a seed's own height, in every round, with or without a reading; and the height of a validator it counted in a reading. A validator that was left out of a reading is not a witness: a height only it has shown has not been read. What the count stands on: with a seed height, the highest seed height of the round, and beside a seed no validator moves that height. With no seed height and a reading from validators alone, the height that more than half of the counted validators have reached. DNO keeps the count in every round in which a seed gives its own height, with or without a reading, and publishes it only with a reading. The count moves up only when one of these is above the height it stands on. When that height is above everything DNO had read before that round, the count starts again in that round. When DNO had already read that height or a higher one (a seed steps up to what a counted validator showed before it), the count runs from the first reading of the highest height DNO had read before that round. So the count never runs from before DNO first read the height it stands on, or a higher one. Seconds are rounded to the nearest; the minutes in the texts are cut. A new height that DNO saw arrive is one thing only: a seed above its own last answer, at the highest seed height of its round (of two seeds that share it, one that rose is enough), and above every height DNO had read before that round. The validators counted in that same round are not compared with it. Only then does status_reason say "Heights advancing" and height_last_advanced_at carry a time; an arrival in a round without a reading is kept, and the next reading publishes it unless the count has moved by then or the claim has ended (below). A node read for the first time, a node back at a height it showed before, a seed that catches up, and a change in which nodes answered are not one. Only a seed is a node. A seed has a last answer, it can rise, and it can fall back. The height more than half of the counted validators have reached is none of these: it changes with which validators answer, so DNO takes it for what the count stands on and for nothing else. With validators alone DNO gives the count and takes no arrival from them. An arrival it saw at a seed before is still reported then, unless the count has moved since or the claim has ended. When the claim of an arrival ends. Once the highest height DNO reads in a round (a seed's, or a counted validator's) is more than 25 blocks below the height that arrived, DNO no longer claims that arrival: "Heights advancing" is not said and height_last_advanced_at is null until the next one. The seed may have taken that very height back, the nodes DNO reads may be on a lower chain, or the seed that showed it may have stopped answering while the others stand far below. One seed that falls back while another still stands within 25 blocks of that height does not end the claim. Neither does a step back of 25 blocks or fewer: an answer up to 25 blocks above the others that its seed takes back in the next round stays claimed, and "Heights advancing" is said for 40 s after it. The count itself is not moved by any of this.
Start-over. The highest height DNO has read can stop being read while the nodes it does read rise below it. A chain on a lower base may be producing blocks, the nodes may be catching up, or that height may have been one answer far above the others; DNO cannot tell which from heights. It gives that height up when the highest seed it reads has kept rising below it for more than 10 minutes: at a rise that comes more than 10 minutes after the rises began. A rise is a round in which one of the highest seeds read is above its own last answer, and above every height it has given since the rises began, whether or not it was the highest then; so a seed that goes back and forth between two heights rises once. The nodes of this rule are the seeds. Of the validators counted beside one seed, only the highest height is taken, as a height read. A round that rests on validators alone is no round of this rule: nothing rises in it, and it begins the 10 minutes again only by reading the held height. So with validators alone DNO does not give a height up, however their height moves: that it went up does not show that a node rose, because validators that stand still on different heights and answer in turn move it too. The 10 minutes begin again when the held height is read (by a seed, or by a validator DNO counts), when a seed gives a height more than 25 blocks below its own last answer, when the highest seed read stands more than 25 blocks above the latest rise without having risen (a seed read for the first time stands), and after more than 10 minutes without a rise. A rise in the round of a fall-back, and the first rise after such a pause, begin the next 10 minutes. A seed that stands above the latest rise by 25 blocks or fewer makes the rising ones wait until they have reached its height; a highest seed that is above its own last answer but not above its highest of the run is neither a rise nor a seed that stands, and the run waits. So a seed or a counted validator that keeps giving the old height, or a seed that stands still more than 25 blocks above the rising ones, holds the count for as long as DNO reads it at least every 10 minutes: DNO cannot tell it from the head of a chain that stands still. Once DNO has given the height up it follows the lower heights: the count starts in that round and runs on by the rule above over the heights DNO reads from then on. It remembers the height it gave up (the first and highest, when it gives up another while it follows) until more than 24 hours have passed since the last time it gave one up. While it does, it does not say that a new height arrived (status_reason does not say "Heights advancing" and height_last_advanced_at is null), and the reading says that DNO follows lower heights: confidence is uncertain, risk is at least elevated, and risk_factors and the summary name it; confidence_reason does too, unless another cause of uncertain comes first. Reading the height given up again, or a higher one (from a seed, or from a validator DNO counts), ends this. The count then runs from the first reading of the height given up, whichever node read it now and wherever the count's own height stands. When the count's own height is above the height given up, the count starts in that round as with any height above everything read, and it is an arrival when a seed rose to it.
degraded — Agreement is moderate, a warning-level public incident is active, or DNO has counted 30 minutes without a new height (height_static_seconds has reached height_standstill_after_seconds). A standstill changes only a reading that would otherwise be stable. Its cause is not known from here: a designed pause and a fault look the same, and so do a chain that stopped and seeds that stopped following it.
unstable — A critical public incident is active, or agreement is weak.
unknown — The Oracle cannot form a reading. This happens when fewer than two public seeds reported their own block height and no validator stood in for the missing one, or when the last observation is more than 300 seconds old. data_quality_reason names which. This is not a health judgment — it is an explicit statement that the Oracle does not have enough information.
Trend
Trend tells you whether the situation is getting better, holding steady, or getting worse, based on comparing the current observation against recent history. It is purely backward-looking — it describes what has changed, never what will change.
Trend compares the current observation with the average of up to 15 previous observations inside a short recent window, and needs at least 10 of them. After a restart or data gap, trend is unknown until enough recent history accumulates.
Risk
Risk summarises the observed conditions that reduce the margin of this reading. It is a snapshot of the present reading, not a forecast.
low — none of the conditions below. elevated — status degraded or unknown, a warning-level public incident, uncertain confidence, moderate agreement, more than one configured seed not answering, or validators standing in for a seed. risk_factors lists the seed, validator, incident, agreement and standstill conditions among these. A fault is not confirmed. high — status unstable, a critical public incident, or weak agreement.
When status is unknown, risk defaults to elevated. The Oracle treats the absence of information as a reason for caution — but not alarm. On a testnet where unknown is a normal occurrence, defaulting to high would desensitize consumers.
Data quality
Data quality measures whether DNO got enough to compare, in an observation at most 300 s old: at least two seeds that reported their own height, or one seed and a validator within 25 blocks of it, or validators alone (see Witnesses). It describes DNO's vantage, not the network.
sufficient — The latest observation holds a reading and is at most 300 s old. insufficient — It holds none (fewer than two seeds reported their own height and no validator stood in), or the last completed observation is older than 300 s (data_quality_reason: too_few_answers, too_few_heights, stale, no_observation; the first two say what the seeds gave). When data quality is insufficient, status is set to unknown.
Confidence
Confidence measures whether the heights DNO compared are close to each other. Besides data quality it has four inputs: the gap between the highest and the lowest height compared, whether a seed's own height is among them, beside one seed whether more than half of the validators that gave a height are within 25 blocks of it, and whether DNO is following lower heights after a start-over (see Status). DNO can have enough fresh heights (data quality sufficient) and still see them far apart. That is a confidence problem.
clear — the heights compared are within 50 blocks of each other, at least one of them is a seed's own, beside one seed the validators within 25 blocks of it are more than half of those that gave a height, and DNO is not following lower heights after a start-over. uncertain — the heights are more than 50 blocks apart, the reading rests on validators alone, half or more of the validators that gave a height are more than 25 blocks from the one seed, DNO has been following heights below a height it read earlier, or data quality is insufficient and nothing was compared. Status and risk are still published.
Agreement
Agreement measures whether the heights DNO compared agree. While at least two public seeds reported their own height, it is computed only from those seeds' own heights; when validators stand in, from the seed and the validator closest to it, or from the counted validators (see Witnesses). A seed's height is its own peerlist entry. A seed that does not list itself has no height here, and neither does a seed that answers with a key other than the one DNO configured for its URL, since that answer may come from another node. A first listed peer's height stays on its /health row (height_source first_peer) and is counted nowhere: not in total_nodes, aligned_nodes, the median or the spread. Seeds that did not answer, and peer-listed identities (never dialed), do not count as disagreement. Agreement is derived from those heights — specifically, how many are within 25 blocks of the median (the middle height, or the upper of the two middle heights when the count is even).
strong — every own height is within ±25 blocks of the median, and the spread is at most 20 blocks. moderate — at least 60% of them, rounded up (2 of 3), are within ±25 blocks of the median. weak — fewer are. unknown — data quality is insufficient: there is no reading, or the observation is stale (data_quality_reason says which). Heights were not compared (aligned_nodes and block_spread are null).
The Oracle publishes the underlying data alongside the label: aligned node count, total node count, median block height, and block spread. At current network size, calculated percentages would be misleadingly precise, so the Oracle publishes counts instead.
Incidents
DNO keeps a public record of its own reading. When its reading stays unknown (a visibility record), degraded or unstable for 3 consecutive rounds, about a minute, it opens a condition record, and it closes the record after 9 consecutive rounds, about three minutes, without that condition. Severities are fixed: visibility and degraded records are warning, unstable records critical. While visibility is poor, no degraded or unstable record is opened: DNO does not describe a condition it cannot see. One exception: on the first round after a start, if the reading is already unknown and no visibility record is open, DNO opens one at once. When every round it has stored since its first unknown one is unknown too, the record is dated from that round; and its text says "since earliest retained public observation" only when nothing earlier is stored. A degraded or unstable record also closes after 9 rounds without a reading: DNO then does not see the condition, and a new record opens if it is still there when the reading returns.
Condition records follow status, so they do not feed it: active_incidents and max_incident_severity leave them out, and active_public_conditions counts them. /incidents lists them with kind: "condition", and /timeline shows them. active_incidents counts any other public-scope incident; the current code opens none of those, so it normally reads 0.
The incidents of DNO's operator's own nodes are fleet-scoped: they are left out of every public count, and /incidents serves public scope only.
status_reason and height_static_seconds. After 30 minutes a reading that would be stable is degraded, and the degraded record that then opens says from when no new height was seen until it opened. A record may carry a dated note that DNO added later; the stored record is never edited.Published observation parameters
DNO's floor, not Demos' forge quorum. These are constants of src/status-rule.mjs (the rule) and src/agent.mjs at the current pin. They are DNO's own bounds, not Demos protocol requirements, and they grade no one.
| Token | Value | What it is |
|---|---|---|
| vantage floor | 2 seeds that reported their own block height; else 1 seed and 1 validator within ±25 blocks of it; else 2 validators, more than half of those that gave a height, within ±25 blocks of their median | else data_quality insufficient |
| cycle | 20 s | MONITOR_INTERVAL_MS |
| probe timeout | 5 s | seed /info abort |
| data_quality cliff | 300 s | kernel |
| consumer stale hint | 120 s | /agent only — not the kernel cliff |
| aligned band | ±25 blocks of upper median | agreement input |
| strong | all aligned and spread ≤ 20 | |
| moderate | ≥ 60% of heights in band | |
| confidence gap | uncertain if height gap > 50 | |
| seed outnumbered | beside one seed, the validators within ±25 blocks of it are not more than half of those that gave a height | confidence uncertain; status still follows the seed |
| static height | 5 min without a new height | status_reason says "no new height for N min"; beside one seed, "the seed has shown no new height for N min" |
| standstill | 30 min without a new height | a reading that would be stable is degraded · height_standstill_after_seconds |
| the count | from the round its height is above every height read before that round; when that height or a higher one had been read before, from the first reading of the highest height read before that round. Its height: the highest seed height; with no seed height, the height more than half of the counted validators have reached | height_static_seconds, in every reading |
| new height seen arriving | a seed above its own last answer, at the highest seed height of its round and above every height read before that round | a node read for the first time, a node catching up, a node back at a height already read, and a change in which nodes answered show none; from validators alone none is taken; no longer claimed once the highest height read in a round is more than 25 blocks below the height that arrived |
| clock start-over | the highest seed read rising below the highest height read (a seed's, or a counted validator's) for more than 10 min, with a rise at least every 10 min; begins again when that height is read, when a seed falls back by more than 25 blocks, or when the highest seed read stands more than 25 blocks above the latest rise; never in a round that rests on validators alone | the lower heights are followed · the height given up is remembered 24 h after the last start-over · confidence uncertain meanwhile |
| restart replay | the stored rounds of the last 24 h | replayed at start by the same rule; nothing is said about height movement until they are |
| witness candidates | at most 8 validators · kept 24 h after their last answer as listed at the seeds' height | joins on such an answer in a counted validator round; leaves when no longer ACTIVE on an agreed list, without that address, or when 8 others come before it; longest-listed first |
| witness read | GET /info · 5 s · 2 MB · no redirects · name lookup 3 s | only in a round where fewer than two seeds gave a height |
| trend window | 10–15 previous observations within the last 24 cycles | else trend unknown |
| 24h static bucket | 5 min | last_24h clock |
| validator round | 60 s | VALIDATOR_WATCH_INTERVAL_MS · its own loop · names the witness candidates |
| validator list | 2 seeds with the same address–status pairs | else no validator figure |
| validator dial | GET /info · 5 s · 2 MB · no redirects · 8 at a time · 200 addresses a round | public http origins only |
| at the seeds' height | ±25 blocks of the seeds' median | the agreement band |
| every round, last hour | 60 min, at least half the rounds counted | else insufficient observation |
How It Works
The Oracle runs a continuous observation cycle. Every 20 seconds, it:
1. Fetches each configured public seed's /info in parallel — whether it answered, its own block height, software version and peer count. The same peerlists feed the catalog of peer-listed identities, which are never dialed; a new identity is kept only after two public peerlists have listed it, and one peerlist brings at most 50 new identities into that count per crawl.
2. When fewer than two seeds gave their own height, reads the witness candidates' /info in the same round (see Witnesses).
3. Computes agreement from the heights compared.
4. Runs the public state computation, which derives the seven values.
5. Opens or closes DNO's condition records (see Incidents).
6. Updates the endpoints with the new reading.
DAHR (the Demos Data Agnostic HTTPS Relay) is attempted on the cross-check RPCs, not on the seeds whose answers enter status. /health reports it in attestation (available, last_count, last_ok_at); last_count is how many of those relays returned a transaction hash, and /organism carries no attestation. Per the Demos docs, each relay call creates an on-chain web2Request transaction; DNO counts a relay when a transaction hash comes back and does not check that the transaction was included. on_chain_publication covers DNO's own posts: fleet alerts, recoveries and daily summaries, never the public reading. While it is disabled, DNO posts none of them. What one DAHR call records is on About Demos.
On a separate loop, once a minute, DNO also reads the on-chain validators table from the same seeds and dials the address each ACTIVE validator published. Those counts are published on /health. While two seeds give their own height they enter none of the seven values; the same round names the validators that can stand in for a seed: see On-chain Validators and Witnesses.
The public state computation is a single function that takes public node observations as input and produces all seven values as output. Fleet records are filtered out inside it and never shape a public value. The computation is deterministic: the same observations, history, open records and observation age give the same values. The rule itself is one file of the source, src/status-rule.mjs.
Public vs Reference Separation
This is the most important structural rule in the Oracle. It governs everything.
Public network (primary observation basis)
All top-level assessments — status, trend, risk, data quality, confidence, agreement — are derived exclusively from public network observations. Public assessment uses the configured Kynesys seed URLs. Independent of DNO: true. Independent of each other: false. One org. When fewer than two of them give their own height, it also uses validators that answer as listed at the address they published on chain. DNO does not check who runs those; one may be its operator's own.
Reference fleet (internal context)
The Oracle operator (XM33) also runs nodes of its own. They provide internal context and infrastructure health monitoring. What DNO reads from them as its operator's, its fleet data, never enters the public assessment and is not part of the public feeds. One of them is a validator on the public testnet: it can enter the public reading only the way any validator can (see Witnesses), and it gets no other weight.
The fleet is a reference layer because it is operated by a single entity and may be on a different chain state than the public network; its state does not represent the public network. The fixnet probe keeps no identity that the public seeds' peerlists list. A fleet node that the public peerlists list appears among the peer-listed identities like any other row, as described under Known Limitations.
How separation is enforced
In the API: the compact agent feed (/organism) contains zero fleet fields. Fleet incidents are tagged with scope "fleet" and excluded from the public incident count. The /incidents endpoint defaults to public scope only.
Fleet views (/dashboard, /history and everything under it) are served on DNO's internal listener only; the public site answers 404 for them.
On-chain Validators
A second question: how many validators the on-chain validators table lists, and how many of them answer DNO at the seeds' height. DNO answers it with four counts on /health, each inside the one above. While at least two seeds give their own height, none of this enters status, risk, trend, confidence, agreement or incidents. When fewer do, the validators that answered as listed stand in for the missing seed: see Witnesses.
The read
Once a minute, on its own loop, DNO sends two read-only calls to the three public seeds, in the Demos SDK's nodeCall format: getValidators, the rows of the validators table still active at the current block, and getNetworkParameters, the parameters in force. The calls go to the address each seed name resolved to, refuse redirects and read at most 2 MB. The rows are reduced on arrival to address, status and published connection URL; stake and timestamps are dropped.
A count is published only when at least two seeds return the same list, meaning the same address–status pairs, and minValidatorStake only when at least two report the same value. When two groups of seeds tie, nothing is published. A seed whose last /info answered with a key other than its configured one is not asked: its answer would be another node's. unstaking counts the rows with status UNSTAKING in that list, and is null when there are none: whether the list carries UNSTAKING rows at all is not confirmed. EXITED validators are not in it.
The watch
DNO then dials GET /info once at the connection URL each ACTIVE row published on chain, as the agreeing seeds list it. The probe rules are the fixnet probe's: only a bare public http origin, connected at the address that was checked, no redirects, at most 2 MB read and a 5 s timeout. The watch adds its own bounds: at most 200 addresses a round, each dialed once, 8 at a time. The places go first to the witness candidates DNO keeps, then to the rows that answered as listed at the seeds' height in the last hour, then to the rest; the last two begin at another row each round. When ACTIVE rows publish more addresses than a round takes, the first two can fill it, and a row that is neither a candidate nor in DNO's record of the last hour is then not dialed (it counts as over the round cap); that record is kept in memory and starts empty after a restart of DNO. Published names are looked up 8 at a time; each lookup keeps its place until it ends, 10 s at most, and no new lookup starts after 20 s of a round. Names not looked up are over the round cap. An https address is not dialed: the probe connects to the address it checked, and a certificate names a host. An origin that several ACTIVE rows publish is dialed once, and its answer names one key, so at most one of those rows can answer as its own key. Published origins that resolve to the same address are dialed once between them; each row is still compared with the keys listed on its own published origin.
| Count | What it means |
|---|---|
| ACTIVE | rows with status ACTIVE in the agreed list |
| answered as the listed key | the published address answered /info, and the answer named the same key as the row |
| at the seeds' height | that answer's own height, its own peerlist entry, within ±25 blocks of the seeds' median from the latest public observation (the agreement band) |
| every round, last hour | answered as the listed key at the seeds' height in every counted round of the last 60 minutes. A round counts when the list agreed and the seeds' median was known. The figure appears once DNO has watched for an hour and at least half the expected rounds counted, and only in a round that counted itself; until then it is insufficient observation. Rounds are kept in memory, so a restart starts the hour again. |
Rows outside those counts are counted by reason: not dialed (no address published, an address that is not a bare public http origin, a published name that did not resolve to a public address this round, agreeing seeds that list different addresses, or over the round cap), no answer, answered with another key, answered without a key, away from the seeds' height, or answered without its own height. Of the rows answered with another key, other_key_shared counts those whose published origin answered as another key listed on that same origin, and other_key_shared_origins how many origins they sit on: those rows share an address with a key that answered there, which is not the same as not answering. The watch also publishes how many distinct published origins it dialed and the most ACTIVE rows that published one of them, and answered_as_listed_seeds: how many of the rows that answered as their own key are among the three public seeds whose answers enter status. That is an overlap, never added to anything. Versions are grouped from the answers as the listed key, and a version is named only in a release form (such as 0.9.9 or 1.0.0-beta.2) and only when at least two validators answered with it; any other text is counted, never shown.
First counted: DNO's clock
Next to the counts DNO keeps one record: for each key an agreed list showed ACTIVE, the time of the first agreed list that did, on the server's UTC clock. The only other value kept with it is the time of the agreed list before it, which the gap rule below needs: no address, connection URL, stake or protocol time. It says when DNO first counted a key, not when the key staked or the chain admitted it. It sits in the same database as DNO's other observations, so a restart keeps it. From it, on_chain_validators publishes how many ACTIVE keys DNO first counted in the last 24 hours, 7 days and 30 days (first_agreed_today, first_agreed_week, first_agreed_month), only while the list agrees, with first_agreed_since, the time of the record's first list.
- The record starts at the first agreed list with at least one ACTIVE key. The keys on that list are its starting point and are never counted as new. A window that starts before that list has no figure: it is null, and
first_agreed_reasonsays why. The homepage shows "not reported", never +0. - A key first seen after more than an hour without an agreed list (DNO stopped, the seeds disagreed, or a write failed) counts in a window only when the list before that gap is inside it. When the gap crosses the start of a window, that window has no figure: the key's first time is not known.
- A key that later leaves ACTIVE still counts where it first appeared. The figures are not net of exits, and they are not the number of validators that joined.
- While a write to the record fails, every figure is null.
The windows run on the server's clock, so a wrong clock moves them. This is not the catalog's first counted, which dates a peer-listed identity from the first public peerlist that listed it, once a second one has. The validators table's own firstSeen field is not used; it has not yet been shown that two seeds return the same value for it.
What the counts do not say
An answer shows that the address a validator published answered DNO, from DNO's vantage, and what that answer said about itself. It does not show that the validator sits in a shard or that consensus uses it. No answer does not change the row on chain: the address may refuse outside callers, or be out of reach from here only. When several keys publish one origin, DNO does not see what stands behind it. If it answers as one of them, the others are counted as sharing that origin with a key that answered, not as silent; if it does not answer, every row on it is no answer. A row that publishes no address has nothing to dial, which says nothing about the validator. "At the seeds' height" is DNO's comparison, not Demos' sync check, and "every round, last hour" is DNO's own record of its rounds, not an uptime figure for the validator.
Unlike peer-listed identities, each of these addresses was published on chain by the validator itself, and the dials run on their own loop, apart from the seed reads that status depends on. The two validator objects on /health hold counts, the seeds' median the answers were compared with (reference_height) and the network's minimum stake as reported: never an address, a key, a connection URL, a validator's own height, a first-counted time for any key, or any validator's stake. When validators stand in for a seed, the reading's spread, and with no seed its median too, come from their heights (see Witnesses); no height is attributed to a validator.
Witnesses: when a seed gives no height
Status needs heights to compare. The three public seeds are URLs of one organization, and when fewer than two of them report their own height the seeds alone give no reading. Since v1.2 DNO then reads validators in their place. A witness is a node DNO reads directly that names its own key and reports its own height: a configured public seed, or a validator that answers as the key the validators list holds, at the address it published on chain.
| Seeds that gave their own height | What DNO does | witnesses.mode |
|---|---|---|
| two or more | The seeds decide, as before v1.2. No validator is read for the reading. | seeds_only |
| one | DNO reads the candidates in the same round. A validator within ±25 blocks of the seed's height counts with it; one further away is left out. With at least one counted there is a reading. | seed_and_validators |
| none | The validators within ±25 blocks of the upper median of the validators' heights are counted. A reading needs at least two of them, and more than half of those that gave a height. Confidence is uncertain. | validators_only |
| otherwise | No reading: status unknown, as before v1.2. | insufficient |
Who the candidates are. The candidates are decided one counted validator round at a time: a round in which two seeds agreed on the list and gave their heights. A validator joins when it is ACTIVE on that list, publishes a plain http address that at least two of the agreeing seeds give alike, and answered there as the listed key at the seeds' height in that round. It stays a candidate for 24 hours after its last such answer. It leaves earlier only when a counted round shows why: it is no longer ACTIVE on the agreed list, it publishes no address or another one, its key is a configured seed's, or eight others come before it in the order below. A round in which it does not answer, or in which the seeds give different addresses for it, removes nobody. At most 8 are kept: those DNO has listed longest come first, so a newly staked key cannot take an older one's place, then those with the most counted rounds in the last hour. Their keys and addresses are kept in DNO's database and never published.
A validator can count with a seed. It cannot count against one. Beside one seed, agreement compares the seed with the validator closest to it, and median_block is the seed's own height: a validator further away changes neither the status nor the median. When the validators within 25 blocks are not more than half of those that gave a height, confidence is uncertain and risk_factors says how many were left out. When the closest validator is more than 20 blocks from the seed, agreement is moderate and the reading is degraded. When the only two seeds that report are more than 25 blocks apart, the reading is unstable as before (21 to 25 blocks apart: degraded; with three seeds, one of them more than 25 blocks from the other two and those two within 25 blocks of each other: degraded); validators are not asked to settle it. The count of the time without a new height stands on the seeds' own heights while one of them gives a height: beside a seed no validator moves it, and the texts say whose count it is ("the seed has shown no new height for N min"). A counted validator's height is then a height DNO has read: when the seed reaches it later, that is not a new height. When no seed gives a height the count stands on the height that more than half of the counted validators have reached, and DNO gives the count and takes no arrival from them.
What is published. /organism and /health carry witnesses: the mode, how many seeds answered and gave their own height, how many validators were read, how many of those answered as listed with a height, how many were counted, and when the list they came from was agreed. Counts and one time: never a key or an address, and no height is attributed to a validator. The reading's own numbers do come from validators' heights when they stand in: with no seed, median_block is the validators' median and the spread is theirs; beside one seed, the spread is the distance to the closest validator. In the two modes with validators, risk is at least elevated and risk_factors says why.
What this does not show. DNO does not check who runs a validator. One validator within 25 blocks of the one seed's height is enough to turn unknown into a reading, and it may be run by the same organization as the seed. With no seed, two validators that agree outnumber one that does not, and no configured seed stands behind the reading: that is why confidence is uncertain then. Whoever runs more than half of the validators counted in such a round decides what height the count stands on, and with it the standstill; the published median is the upper median of every validator height read, counted or not (when only two gave a height it is the higher), so it can be above the height the count stands on. With validators alone the count waits for the majority: when its height is below the one the count stood on (the seeds that stopped answering were ahead, or other validators answer now), DNO says no new height until the majority has passed it, though counted validators show higher heights meanwhile. Beside a seed, a counted validator can make a height the seed reaches later older, never newer; while the one seed stands still, validators that rise near it do not restart the count; and a seed that stands behind them by as many blocks as arrive in one round, or more, is not said to show a new height. A validator DNO left out of a reading (one alone, or one more than 25 blocks from the one seed) is not a witness: its height has not been read, and a height only it has shown still counts as new when a seed or a counted validator first shows it. Heights are compared; block hashes are not, so two nodes at the same height on different chains would look the same.
What the count does not show. A height counts as new when DNO first reads it: when the nodes it reads were behind or did not answer, blocks produced earlier show as new heights later. When a node DNO had not read before, or validators it had not counted before, show a height above the one the count stands on, DNO cannot tell how long that height has stood. It claims no arrival and counts by the rule above: from that round when the height is above every height read. A standstill can then read stable again for up to 30 minutes after each such first reading: this happens when the seeds stop answering during a standstill and the validators that stand in are a block ahead of where the seeds stood. After a chain restarts at a lower height, DNO reports no new height until the highest seed it reads has risen for 10 minutes, and then follows the lower heights. That takes longer while a seed or a counted validator still gives the old height or a seed stands above the rising ones, and the 10 minutes begin again each time a seed falls back by more than 25 blocks. While no seed gives a height it does not come at all: with validators alone DNO gives no height up, so after a restart at a lower height it reports no new height, and a degraded reading from 30 minutes on, until a seed gives its height again and has risen for 10 minutes, until a seed or more than half of the counted validators have passed the height the count stands on (the count then moves by the rule above), or until no validator can stand in any more (24 hours at most). One round is enough to bring DNO there: a round that rests on validators alone in which most of those that answer stand above the other candidates or, after DNO has followed lower heights, still stand on the height it gave up. That round moves the count there or ends the following, and with validators alone DNO does not give that height up again. The other way round a standstill could read stable when validators that stand still on four or more heights answered in turn, and a standstill is what the count is for. DNO cannot tell a chain restarted lower from nodes that are catching up, or from one answer far above the others: in each case, once it gives the height up, it follows the lower heights, reads uncertain, and for 24 hours does not say that a new height arrived, unless the old height is read again. When the old height is read again without the count's own height passing it, the old count is read until the count's own height has passed the old height, or steps up after a height above the old one was read, or until DNO gives the old height up again. How long that is depends on who read it. When the nodes DNO follows reach it themselves (a seed lands exactly on it, or a validator counted beside it shows it a block ahead), it is one or two rounds at a 10 s block time, more with slower blocks or a seed that pauses. When a validator that still stands on the old height is counted, it lasts until the followed heights have climbed to it: up to 25 blocks, 13 rounds at a 10 s block time. When a seed that still stands on it answers, it lasts until the next start-over, 10 minutes of rises or more, and for as long as that seed keeps answering. A degraded record that opens in those rounds is dated from the first reading of the old height. One answer of one seed far above the others is enough for all of this. DNO takes it as a new height that arrived, and height_last_advanced_at carries its time until the first round in which nothing DNO reads is within 25 blocks below it, usually the next. In the round of that answer the seeds stand more than 25 blocks apart, so the reading is not stable (unstable with two seeds; with three, degraded when the other two are within 25 blocks of each other and unstable when they are not; with one seed beside validators there is no reading), and status_reason does not say "Heights advancing". The count starts again from that answer either way, so a standstill reads stable again for up to 30 minutes and the next degraded record is dated from that answer. Until DNO has given that height up it reports no further new height, though blocks are produced; while every seed stands still it never gives it up, and with validators alone it gives none up. A restart of DNO keeps the last 24 hours of rounds: a standstill older than that is counted from the oldest round kept; a height read only before those 24 hours counts as new when it is read again; a height given up that was last read before them is forgotten, also when DNO gave it up since, so the reading no longer says that DNO follows lower heights and an arrival can be claimed again; and a node not read in those 24 hours is a node read for the first time. A candidate that stops answering keeps its place among the 8 for up to 24 hours. Validators stand in for at most 24 hours at a time: a candidate is renewed only by a round in which two seeds agree on the list and give their heights, so after 24 hours with fewer than two seeds answering no candidate is left, and the reading is unknown until two seeds answer again. DNO's operator runs a validator on the public testnet; it is a candidate under the same rule as every other, and only if it publishes an address on chain.
Turning it off. The server setting that stops DNO's dials to validators stops these reads too. The reading is then made of the seeds alone, as before v1.2. A fault in DNO's own read. When a read of a validator ends in a fault of DNO's own, that read says nothing about the validator. No validator is then counted in that round's reading, and the validator watch gives no counts for a round with such a dial (validator_watch.state read_fault). When DNO's own read of a seed ends in such a fault, the reading still counts that seed as not answered: status_reason and the summary can say "fewer than 2 public nodes answered" although the fault is DNO's. The seed's row on /health then has error internal error: a TypeError or a ReferenceError inside DNO's read. The other values there (timeout, connection failed, HTTP 503 with the status sent, invalid response, response too large) are what DNO saw of the seed; a fault of another kind in DNO's own code would show under one of them.
Clocks, kept apart
DNO keeps several clocks. None is added to another. The validator watch's rounds and first-counted times do not enter status; validators' heights do only when they stand in for a seed.
| Clock | What it times | Where it shows |
|---|---|---|
| the public round, every 20 s | the three public seeds' /info, peerlists included | status and /organism |
| heights | each seed's own height in every round, the heights of the validators counted in a reading, the highest height read and when it was first read, and since when the count runs | agreement, height_static_seconds, the standstill limit, chain movement, "at the seeds' height" |
| the watch round, once a minute | the validators list, and the dials at the addresses ACTIVE rows published | the validator counts; "every round, last hour" counts these rounds |
| catalog first counted | when a public peerlist first listed an identity that DNO keeps (it keeps one once a second peerlist lists it). The first hour of the count is its starting set | the peer-listed identities' today, week and month; a window the count does not cover, or that a gap in the count crosses, is not reported |
| ACTIVE first counted | the first agreed list that showed a key ACTIVE | the ACTIVE today, week and month |
The first-counted clocks and every window run on the server's UTC clock; heights are the seeds' own and, when validators stand in, theirs. The validators table's own times, firstSeen and validAt, are not used.
Interpretation Model
The Oracle keeps what the reading showed apart from how much margin it has:
• Status describes what the nodes in the reading showed this cycle: the public seeds, or the validators standing in for a missing one.
• Risk is a level set by the observed conditions that reduce the margin of that reading; risk_factors names some of them.
• Agreement measures alignment among the heights compared.
• Peer-listed identities are never dialed and do not enter status, risk or agreement.
• On-chain validator counts come from the seeds' validator list and DNO's own dials. The counts do not enter status, risk or agreement; the validators that answered as listed can stand in for a missing seed.
No rule changes status because a seed is silent; the agreement of the remaining heights can change it. A silent seed shows in the seeds line and, when more than one is silent, in risk. With the three configured seeds, two silent seeds leave one height: the reading is then made of that seed and the validators within 25 blocks of it, with elevated risk, and it is unknown when there is none.
Explanation Layer
The Oracle provides explanatory fields alongside its categorical assessments. These fields are intended to improve interpretability for human readers and machine consumers.
status_reason
Explains the primary observed basis for the current status. Examples: Heights advancing; public nodes aligned, Public nodes aligned; no new height for 12 min, No new height for 31 min; public nodes aligned (a standstill: degraded), The seed has shown no new height for 31 min; one public seed and 2 validators aligned (the same beside one seed), Heights advancing; one public seed and 2 validators aligned, Insufficient data: fewer than 2 public nodes answered. In these strings "public nodes" are the configured seeds.
risk_factors
Lists the observed conditions that reduce the margin of this reading. The array may be empty.
agreement_reason
Describes the alignment basis used in the agreement assessment. Example: 3 of 3 public nodes with a height within ±25 blocks of the median (spread: 0 blocks); with validators standing in: One public seed and the closest of 2 validators within ±25 blocks of it (spread: 1 block); when heights were not compared: Not compared: fewer than 2 public nodes reported their own block height.
confidence_reason
Explains why confidence is clear or uncertain. Typical values: Observed public signals agree (clear), No cross-check: fewer than 2 public nodes answered (uncertain, insufficient data), No public seed reported its own height: the reading rests on validators alone (uncertain, validators only), 3 of 4 validators that answered as listed with a height are more than 25 blocks from the one public seed (uncertain, the seed stands with a minority of the validators that gave a height), DNO has been following heights below a height it read earlier, and cannot tell why from here: a chain restarted lower, nodes catching up, and one answer far above the others look the same (uncertain, after a start-over), or Public nodes report block heights more than 50 blocks apart (uncertain, divergent heights).
These explanatory fields summarize observed conditions. They are not guarantees or operational advice.
Historical Metrics
The Oracle displays rolling historical metrics derived from its own observation cycles over the current history window.
uptime_7d — percentage of observed cycles in the last seven days in which the configured seed answered (seeds only; catalog identities are never dialed).
avg_latency_7d — average response latency across successful observed cycles.
These historical metrics are observational summaries of the configured seeds, not statements about whoever runs them. uptime_7d and avg_latency_7d stay null until more than ten rounds are recorded for a seed. Historical metrics do not change the core assessment model unless explicitly stated.
Last 24 Hours Summary
The "Last 24 Hours" panel on the homepage surfaces aggregate observation from the most recent 24 hours of probe cycles. All fields derive from the public node history table and the public-scope incidents table.
Public nodes observed
How many seeds reported their own height in each round. It reflects what the Oracle observed, not the number of validators in the network. "Typically N" (typical_set_size) is the most common count across the window; "peak M" (peak_set.size) is the largest. Rounds recorded before 1.1 stored a different count (every height a seed returned, or the number of configured seeds when heights were not compared), so for 24 hours after the update the window mixes the two.
Peak observation window
When the typical and peak counts differ, this row reports how often the larger set was assembled and how many seeds answered on average in those rounds. It frames observation quality, not network failure: "3 nodes observed; 3.0 answered on average across 156 cycles" means that in 156 rounds of the last 24 hours three seeds reported their own height, and three answered on average in those rounds. Under the 1.1 rule the average is never below the count, since a seed must answer to report its own height; rounds recorded before 1.1 can make it lower during the first 24 hours after the update.
Chain movement
Categorical: normal, interrupted, or unknown. Computed by bucketing the 24-hour window into 5-minute intervals and checking whether the seeds' median at the end of each bucket (the median of the seeds' own heights; with one seed, that seed's height) is higher than at the end of the previous one. A round that rested on validators alone records no such median: it is not part of chain movement or of blocks_advanced, and a window in which validators stood in for both seeds has fewer buckets to compare. Normal requires an advance in at least 95% of the compared buckets and no static run of 5 minutes or more; since one bucket without an advance is already a 5-minute run, every compared bucket must advance. Interrupted fires on either threshold. Unknown indicates fewer than 12 buckets of data — typically during partial windows — or that rounds recorded under the own-height rule do not cover the whole window yet (reason own_height_record_shorter_than_window): for 24 hours after that rule starts or restarts on a server that holds rounds an older version recorded, chain movement and blocks_advanced are not published, because a figure from part of the window would stand next to a coverage that counts all of it. blocks_advanced is the newest such median minus the oldest, over rounds with a reading, as observed; it is null when chain movement is unknown, when there is no pair to compare, when the newest median is below the oldest, or when the median fell by more than 25 blocks between two rounds, and it implies no target rate.
Longest non-stable interval
The longest contiguous run of cycles where the Oracle's computed status was "unstable" or "degraded". Since v1.2 that includes a standstill of 30 minutes or more. Cycles in "unknown" state break the run because "unknown" indicates an observation gap, not network state; so does a gap of more than three rounds without an observation.
Critical public incidents
active_critical_public_incidents counts critical public incidents active now; critical_public_incidents_in_window adds those resolved inside the window. Neither counts fleet incidents or DNO's condition records (active_public_conditions and /incidents report the condition records).
Coverage
The fraction of expected probe cycles actually observed in the window (86,400 seconds / 20-second cadence = 4,320 expected cycles). When coverage is below 50%, sufficient is false and only the coverage figures and a message are published until the rolling window is sufficiently observed.
Scope and Limits
The Oracle is a watch-only observability system. It summarizes observed network conditions and historical measurements but does not provide financial, operational, legal, or governance advice.
A row in DNO's outputs is an observation of an identity or an endpoint, not a statement about whoever runs it.
Oracle outputs are based on externally observable signals available during each measurement cycle. Public endpoint availability, temporary network conditions, or incomplete visibility may affect results. Outputs should be interpreted as structured observed assessments, not guarantees of present or future validator behavior.
Understanding Uncertainty
The Oracle treats uncertainty as a first-class output. On a young testnet with small, evolving node coverage, partial visibility is a normal operating condition — not an error.
The Oracle starts from caution and earns stronger claims through sustained observation. It prefers honest uncertainty over artificial precision.
Three kinds of uncertainty
Insufficient data. The Oracle does not have enough information to form an assessment. Too few public seeds answered or reported their own height and no validator stood in, or the Oracle just started and hasn't completed a full cycle yet. Effect: data quality → insufficient, status → unknown, risk → elevated.
Stale data. The Oracle has data, but it may be too old to reflect current reality. Staleness means DNO's own round did not complete: the process stopped, or a round failed inside DNO. A seed that does not answer does not cause it. The Oracle always publishes how many seconds ago it last observed the network (staleness_seconds) so consumers can apply their own freshness judgment.
Divergent heights. The Oracle has fresh, sufficient data, but the own heights it compared are more than 50 blocks apart. Effect: confidence → uncertain; data quality stays sufficient, and agreement_reason gives the spread. With fewer than two heights there is nothing to compare: that is insufficient data, not divergence.
The unknown contract
When the Oracle reports unknown, insufficient, or uncertain for any field:
1. The categorical value is stated as a word. In the reading, counts that could not be computed are null or left out, never 0 or a guess (before the first observation, staleness_seconds counts from the agent's start). Three older fields of /health validator_growth are kept as they were for 1.x clients and do use 0: network_head when no seed gave a height, and today, week and month, where first_counted gives null.
2. No guess, default, or cached value is substituted.
3. Consumers can rely on these states as stable, intentional signals — not bugs.
Known Limitations
DEMOS is a young testnet. The Oracle is designed for this reality and makes no claims beyond what it can observe.
Small public node set. The Oracle currently reads three public Kynesys seeds, and at most 8 validators in their place when fewer than two of them give a height. Assessments use coarse-grained categories that match this resolution. As the network grows, finer granularity may be added without structural changes.
Two of the three seeds share a host. kyne-node3 and kyne-node3b are two ports on one host (see Sources). When those two are the seeds that answer, a reading of the seeds alone rests on that one host, and no validator is read in that round.
Peer-listed identities. The public seeds' peerlists name other identities. DNO keeps each one after two public peerlists have listed it, records its first and last listing, and never dials it. A row is a peerlist observation: not a row of the on-chain validators table (which DNO reads separately, for counts only), not a census of Demos beta, and not a promise of monitoring. The peerlists also carry a second status flag whose meaning DNO has not confirmed; it is not published. In one crawl, one peerlist brings at most 50 identities DNO has never seen into the count; the rest wait for a later crawl. That bounds how fast one peerlist can grow the waiting set or, with a second peerlist, the catalog. The total is bounded by the 2,000-row cap, where rows no public peerlist has listed for 30 days make room. The today, week and month figures count identities first counted after the count's first hour. That hour is the starting set: what the peerlists already listed when DNO began to count, which is not growth. An identity that an older DNO record already held is not counted as new either, nor is one that a peerlist listed in its first read after DNO started, before DNO kept it: it may have been listed there before. A window is not reported while the count does not cover it, or when a peerlist that listed a new identity before DNO kept it had not been read for more than an hour across the window's start: the identity may have appeared at any time in between. One case is not covered: a restart makes DNO forget the identities it had seen on one peerlist only, and if a peerlist DNO could not read since that restart lists such an identity only after DNO kept it, the identity stays counted. None of these figures says when an identity joined the network.
Why DNO does not dial them. An answer would show only that an address a peer advertised answered DNO: not that the identity is a validator, that it sits in a shard, or that the network has more capacity, and the count would still not be a census. Status is made of the configured seeds, and of validators only under the witness rule: a validator is named by the on-chain list two seeds agree on, and it published its own address there. A peerlist entry is neither, so answers from peer-listed identities could not enter status without letting whoever controls those listings move it. And the addresses are chosen by the peers: dialing hundreds of them every round, in the loop that reads the seeds, would compete with the seed reads that status depends on.
The catalog lists every identity it kept that is not a configured seed. That includes any node run by DNO's operator that the public peerlists list; such rows are displayed like every other row and are not otherwise identified.
Observable coverage only. The Oracle can only assess what it can reach. Nodes that are firewalled, on private networks, or not yet discovered are invisible. The Oracle is honest about this — unknown and insufficient are always explicitly stated.
No prediction. The Oracle describes what is, not what will be. Even trend is backward-looking. Predictive signals may be added as a separate, clearly labeled layer in the future — never merged into the core assessment model.
DNO informs context; it does not advise, predict, score, certify, or decide action.
Methodology Changelog
v1.2 — 2026-10-05. Witnesses. When fewer than two public seeds report their own height, validators that answer as listed stand in for the missing seed: beside one seed, a validator within 25 blocks of it counts with it; with no seed, at least two validators, more than half of those that gave a height, within 25 blocks of their median give the reading, with confidence uncertain. /organism and /health gain witnesses, which says what the reading rests on. Before, the reading was unknown whenever fewer than two of the three seeds gave a height. Standstill. A reading that would be stable is degraded once DNO has counted 30 minutes without a new height (height_standstill_after_seconds), and the degraded condition record says from when. Before, a static height never changed status: on 2 October the page read stable through most of a three-hour standstill. A new height that DNO saw arrive is a seed above its own last answer, at the highest seed height of its round and above every height DNO had read before that round; a node read for the first time, a node that catches up, a node back at a height already read, and a change in which nodes answered show none, and once the highest height DNO reads in a round is more than 25 blocks below the height that arrived, that arrival is no longer claimed. status_reason says "no new height for N min" where it said "height unchanged for N min", and beside one seed "the seed has shown no new height for N min". height_static_seconds is published in every reading: how long DNO's count has run (see Status). It never runs from before DNO first read the height it stands on, or a higher one. A round without a reading, and an observation gone stale, publish neither height_static_seconds nor height_last_advanced_at. Before, a seed that had fallen behind and caught up counted as an advance, and on 2 October that moved the published last advance by an hour. The fixnet probe keeps and dials no identity that the public seeds' peerlists list. Disclosure. DNO's operator runs a validator on the public testnet (What the Oracle Is). Records. The visibility record of 1 October 2026 carries a dated note on /incidents and /timeline: its cause was a fault in DNO's own read. /sentinel says whether DNO's alert process completed a check lately, and nothing else. The degraded and unstable summaries read "1 of 3 public nodes did not answer" (it was "public node"). When no seed answered, risk_factors says "No public seed answered" (it was "Only 0 of 3 public nodes answered — limited cross-checking"). /docs no longer lists /signals, which was never served. Reads. When a read of a validator ends in a fault of DNO's own, no validator is counted in that round's reading, and the validator watch publishes the state read_fault and no counts for that round. Chain movement and blocks_advanced are made of the seeds' median only; a round that rested on validators alone is not part of them. Smaller changes. /docs lists every route the site serves. On /incidents, detectedBlock and resolvedBlock are null when no height was recorded (it was 0). The height signal of /health reads "Highest own height among the seeds that answered" (it named the public network). A seed's version label is published only when it reads as a release version. /version, /docs and /federate give DNO's own version as 7.2 (they still said 6.9, the label of an older release). /reference says "not reported", "not probed" or "no answer" where it showed a dash. The date is the methodology's; /timeline gives the day each API version was first served (1.1: 2026-10-02).
v1.1 — 2026-09-28. Seeds are counted toward data quality and agreement only when they report their own block height (their own peerlist entry); a seed that does not list itself has no height there, and its first listed peer's height is shown on its /health row only. A new peer-listed identity is kept only after two public peerlists have listed it. The peerlists' unconfirmed second status flag is no longer published. /dashboard and /history are internal only. last_24h.chain_movement.blocks_advanced reports blocks advanced as observed, with no target rate; validator_growth.mixed_fields labels the two 1.0 counts that add seeds and peer-listed identities together. Staleness and last_updated are measured from the last completed public observation. Status reasons name the cause of insufficient data and say "height unchanged for N min" instead of "blocks advancing" when heights are static. Trend history is bounded to a recent window. The 24-hour chain-movement buckets no longer read a single-row bucket as static. active_public_conditions counts DNO's own condition records, which active_incidents excludes; the two last_24h critical-incident counts leave them out too. A seed that answers with a key other than the one configured for its URL gives no height. Chain movement and the static-height clock use only rounds recorded under the own-height rule. Rows recorded before 1.1, when DNO also read its own node's peerlist, stay hidden until two public peerlists list them, and the first time published for them is then the first of those listings. The peer-listed today, week and month leave out the count's first hour (its starting set), identities an older record already held, and identities a peerlist listed in its first read after a start, before DNO kept them; validator_growth.first_counted gives them with null for a window the count does not cover or that a gap in the count crosses. Chain movement in the 24-hour summary is not published until rounds recorded under the own-height rule cover the window. In one crawl, one public peerlist brings at most 50 never-seen identities into the two-peerlist count.
On-chain validators — 2026-09-29. /health gains on_chain_validators (the validators table as the public seeds list it: counts by status and minValidatorStake, each only when two seeds give the same answer) and validator_watch (one /info dial a minute to the address each ACTIVE validator published on chain, counted). Neither enters status or /organism; the /organism contract is unchanged.
Corrections — 2026-09-29. The descriptions of incidents, confidence, risk, agreement, DAHR and the 24-hour summary were rewritten to match the code at this version. The rule changes they describe are the 1.1 changes listed above.
v1.0 — 2026-06-11. Initial versioned methodology. All numeric thresholds stated on this page (5-minute buckets, 12-bucket minimum, 95% advance ratio, 5-minute static-run limit, two-node reachability minimum) verified against source code on this date. Going forward, any change to the assessment logic receives a dated entry here; changes that affect the /organism contract also receive an x-changelog entry in the schema.
How to Use the Data
For builders and operators
The homepage at / shows the live reading: status with its reason, the validators on chain, the five inputs and what status is made of, each seed against the median, the last 24 hours, the peer-listed identities and the probed seeds, incidents, and the API.
The /health endpoint provides full diagnostic data — canonical fields at the top level, then derived detail: public seeds (with height_source), signals, validator_growth (seed counts and peer-listed identities; its online and synced add both populations and are listed in mixed_fields), the attestation state, and the on-chain validator counts (on_chain_validators, validator_watch).
For agents and automated consumers
The /organism endpoint is the primary agent feed. It returns 17 required top-level fields (plus additive 1.1 and 1.2 fields) and no fleet data. Most fields are flat values; risk_factors is an array and last_24h is a small summary object. Every top-level field is always present. The 17 required fields are never null; the additive 1.1 fields (observed_at, data_quality_reason, height_last_advanced_at, height_static_seconds) are null when there is nothing to report. witnesses (1.2) is always an object; its validators is null when no validator was read.
The simplest consumption pattern is the minimal triple: read status + risk + confidence. These three fields give DNO's reading in three values. Your agent decides what to do with them — the Oracle never tells you.
Before relying on a reading: data_quality says whether DNO had a reading, witnesses.mode what it rests on, and staleness_seconds how old the observation is. When data quality is insufficient, status is unknown and risk elevated by design.
For open records: active_incidents and max_incident_severity describe public incidents, and active_public_conditions counts DNO's condition records, without fetching /incidents.
Agent feed: /organism — 17 required fields, public-only, a new observation every 20 seconds (ETag / 304 between observations).
Contract: /organism/schema — JSON Schema, stability policy, changelog. The schema is the binding contract; this page is the explanation. If they ever disagree, the schema governs and the discrepancy is a bug.
Full diagnostic: /health — canonical + derived.
Incident log: /incidents — public scope; condition records marked
kind: "condition".Peer-listed identities: /catalog — identities listed on the public seeds' peerlists;
/catalog/lookup?key= checks a full key exactly, against the catalog and, while two seeds return the same list (read in the last 300 s), the validators list.API documentation: /docs
Agent guide: /agent
Oracle sources: /sources — where the observations come from.
Observation timeline: /timeline — generated public observation record.
Reference surface: /reference — the fixnet probe and the peer-listed identities, in two tables (not part of the core assessment).
API version:
1.2