Finding F21 · relay registration & reachability

Every pool's relays, checked

A stake pool's relay set is the one piece of its infrastructure anyone can verify without trusting the operator — it is registered on-chain, in the same certificate as the pledge and the margin. This page reports what every current pool registered, which pools share infrastructure, and which endpoints answered a real Cardano handshake.

Loading the published cut…

Read this before you quote a number#

This dataset is deliberately not a scoreboard. It has two layers and they are kept apart on purpose, because only one of them is durable.

Fact

What each pool registered on-chain, when, and which endpoint strings more than one pool advertises. Reproducible by anyone with db-sync.

Observation

That an endpoint did or did not complete a handshake with our prober, at that moment, from one network location. It expires.

Not claimed

That any relay is “offline”. Who operates any pool. That shared infrastructure means shared ownership. That any operator is negligent.

“Unreachable” is not “offline.” A firewall that drops our prefix, an inbound connection limit, a rate limiter, a node restart or a transient route all render identically here. We measured the same endpoint answering from one host and timing out from another minutes later. Every reachability number on this page is one sweep from one vantage point, and it is published with its timestamp for exactly that reason.

What pools registered#

The durable layer. A pool's live registration is its latest pool_update; a retirement counts only if it was announced after that update and its epoch has arrived.

RegistrationPoolsADA delegated

Every number above aggregates relay_pool_health.csv, extracted by build_relay_registration.sql. Reproducible from db-sync alone: run it and you should get these exact rows.

What answered#

A two-pass node-to-node handshake sweep of every registered endpoint — a real Cardano handshake plus a tip request, not a TCP connect. Reachable hosts are counted as distinct resolved hosts, never as registration entries: two DNS names pointing at one machine is one relay.

ObservedPoolsADADelegators

Unreachability tracks stake almost monotonically. A uniformly broken probe would not produce that gradient — pools abandoned without ever filing a retirement certificate would.

Active stakePoolsNothing answeredShare
A high reachable-host count is not proof of anything on its own. The endpoints have to actually belong to the pool. At least one pool here scores the top grade in this table on relays it does not run — see pools advertising somebody else's infrastructure.

Why each endpoint failed. no_ipv6_at_probe means our prober has no IPv6 route and did not test the endpoint at all — those pools are reported as untested, not as unreachable.

FailureEndpoints

Every number above aggregates relay_endpoint_status.csv, relay_pool_health.csv, produced by relay_probe.py, build_relay_reachability.sql. This half is a live measurement, so your own run will differ from this one.

Dead pools are not the story#

Read on its own, the count of pools where nothing answered is badly misleading — most of them are abandoned registrations whose operator never filed a retirement certificate. They inflate every per-pool count of Cardano's “3,000 pools” and they hold almost no stake. The honest cut is pools that actually produced a block in the last 30 epochs.

ObservedMinting poolsADAIdle poolsADA

Aggregates relay_pool_health.csv. Mixed: the block counts come from db-sync via build_relay_reachability.sql and reproduce exactly; the reachability columns come from the sweep and will not.

What a pool used to advertise#

Pool registrations are append-only. A pool can change what it advertises; it can never un-publish what it advertised before. That matters, because removing a relay from the certificate is an ordinary transaction — no new deposit, just the fee — and it makes an unreachable relay stop being unreachable by making it stop existing. This is the one view where publishing less makes a pool more visible, not less.

DirectionCertificatesPools

Every pool that registers no relay and is producing blocks — whether it removed relays it once had, or never advertised any:

TickerADADelegatorsBlocks / 30epPreviously advertised

Stake is a current snapshot; blocks are a 30-epoch total. A pool that recently lost delegation will show many blocks against little stake — BD3 held 56.7M ADA until epoch 646 and about 190k after it, and earned its blocks on the former. That is a change in delegation, not an anomaly.

It is not just dead pools

The obvious dismissal is that pools without relays are abandoned ones nobody retired. Seventeen of them are exactly that — they hold 47,625 ADA between them and have produced nothing. But by rate, the behaviour peaks again among established mid-size pools with thousands of delegators.

Pool sizeNo relayAll poolsRateShare

Registering no relay is permitted, and operators who do it usually cite DDoS surface. What it means factually is that the network cannot discover them from the chain, and their inbound load sits on the pools that do publish. Every one of these events carries its transaction hash in relay_registration_changes, so none of it has to be taken on trust.

Every number above aggregates relay_registration_changes.csv, relay_pool_health.csv, extracted by build_relay_registration.sql. Reproducible from db-sync alone: run it and you should get these exact rows. Certificate history is append-only, so it is the same for everyone, forever.

Pools advertising somebody else's infrastructure#

Reachability alone can be gamed, and until this section existed this dataset was rewarding the gaming. A pool that registers the founding entities' bootstrap backbones scores the best available grade here — many reachable hosts, all at the tip — because those endpoints genuinely do answer. They are IOG's, the Cardano Foundation's and Emurgo's. It also slips past every sharing check on this page: no other pool registers the same strings, so endpoint, resolved-host and domain matching all see nothing. Spotted by @HephyPool.

TickerADADelegatorsPledgeBlocksForeign / totalAdvertises
Why this needs no judgement call about who is who. The backbone.* names are the bootstrapPeers shipped in the stock mainnet topology.json — the network's shared entry points, published so any node can find its first peers. They are nobody's relays, and the founding entities' own pools do not register them either: IOG1 advertises iog1-relays.cardano.iog.io, the CF pools advertise cfNrN.mainnet.pool.cardanofoundation.org. So no allowlist of identities is needed to read this table.

That a pool registered these strings is fact, straight from the chain. What it means is the reader's call: a misconfiguration and a deliberate free-ride look identical on-chain and this table does not try to separate them. What it does mean is that the pool publishes no way to reach its node, and its inbound load sits on infrastructure it does not run.

Every number above aggregates relay_foreign_infrastructure.csv, extracted by build_relay_foreign_infra.sql. Reproducible from db-sync alone: run it and you should get these exact rows.

Where the relays actually live#

A pool with three relays is not redundant if all three sit in one datacenter. Pool counts cannot see that; the network announcing each reachable relay IP can.

NetworkCountryPoolsADAWholly insideTheir ADA
An ASN is a failure domain, not an operator. Hetzner, OVH and Contabo host a large share of the hobbyist internet, and two pools in one datacenter are usually two unrelated people who both picked the cheap option. That is exactly why it counts: uncoordinated concentration is still concentration, and a provider outage does not care whether the pools behind it were coordinated. Pools whose relays we could not reach sit in no network here, so every figure is a floor.

Aggregates relay_asn_concentration.csv, produced by relay_asn_lookup.py, build_relay_concentration.sql. This half is a live measurement, so your own run will differ from this one. ASN ownership comes from Team Cymru's public IP-to-ASN DNS service.

Shared infrastructure#

Endpoints advertised by more than one current pool. This is a shared string: strong evidence of shared infrastructure and no evidence at all of shared ownership. A hosting provider, a relay-as-a-service, a white-label operator and one person running twelve pools are indistinguishable here. Delegator counts separate the patterns that a pool count alone hides.

EndpointPoolsADADelegatorsTickers

Registration strings understate sharing. One fleet in this dataset gives every pool its own numbered hostname under a single domain, so string matching sees the pools as unrelated. Grouping by the IP those names actually resolve to collapses it. This is a floor, never a ceiling — an endpoint we could not resolve contributes nothing.

Resolved hostPoolsNamesADATickers

Aggregates relay_shared_endpoints.csv, relay_shared_domains.csv, relay_shared_hosts.csv. Mixed: the endpoint and domain tables come from db-sync via build_relay_registration.sql and reproduce exactly; the resolved-host table needs a sweep (build_relay_reachability.sql) and will not.

Why a single-pass probe lies#

Four measured traps. Each one produced a confident, wrong answer during the build before it was caught — which is the whole reason the method is published alongside the numbers.

  1. Probe wall time is not handshake time, and there is no fast path. backbone.cardano.iog.io answers in 5.2s; Trust Wallet's Kiln-hosted relays take 14–30s — both reporting a protocol RTT near 100ms. A first sweep at a 10s timeout marked TW001, with 113.6M ADA delegated, as having no reachable relay. It answers fine. Every non-DNS failure now gets a slower, lower-concurrency second pass, and only a failure in both passes is recorded.
  2. Never assume port 3001. The registered port is authoritative and operators use anything — this dataset contains relays on 19002, 6010, 5001 and 1338. Probing the wrong port produces a confident false negative.
  3. A multi-host SRV entry is not “one relay”. Cardano's multi-host relay type registers a bare domain whose real record is _cardano._tcp.<domain>, and each target carries its own port from DNS. cardano-cli ping does not resolve SRV at all: hand it one and it does a plain A lookup on port 3001 and reports a bogus failure.
  4. The tool's error text conflates DNS and connection failures. Both say “does not exist”, so naive parsing labels every refused connection a DNS failure. The cause has to be read from which socket call failed, and from the IPv4 attempt where one exists.

The prober is relay_probe.py; the full method and its limits are 27_RELAY_HEALTH_METHOD.md and the graded write-up is F21_relay_registration_and_reachability.md.

Take the data#

All of it is in the repo — no API key, no rate limit, no server to ask. Clone it or pull a CSV straight from raw.githubusercontent.com.

TableWhat it holds
relay_pool_healthOne row per current pool: registration shape, reachable hosts, at-tip hosts, shared-endpoint flag, last_checked.
relay_shared_endpointsEndpoint strings advertised by more than one current pool.
relay_shared_hostsPools sharing a resolved IP — catches per-pool hostnames.
relay_shared_domainsPools sharing a parent domain (heuristic; groups hosting customers too).
relay_asn_concentrationPer network: pools, stake, and how many have their entire reachable relay set inside it.
relay_foreign_infrastructurePools advertising founding-entity bootstrap backbones as their own relays.
relay_registration_changesEvery certificate that changed a pool’s relay count — date, tx hash, before/after, direction.
relay_endpoint_statusThe raw per-endpoint sweep, with failure cause and detail.

Method and limits — which bind any use of these numbers — are in docs/27. The graded write-up is F21, the build is sql/56_relay_health. For an operator-clustering signal that does not depend on registration strings at all, see F10 — synchronized KES rotation, read from block headers.

Registration rebuilds from db-sync in about two seconds and is fully reproducible. The sweep is a live measurement and will not reproduce exactly — that is the nature of the layer, and why the two are kept in separate tables. Readers who want to argue about decentralization now have the numbers to argue with; the argument is theirs.