The consumer-side observed layer for MRC-13: provider, ASN and region for the Monad validator active set, measured from the network rather than declared, so a validator's stated infrastructure can be cross-checked against what the network actually shows.
Monad's MRC-13 metadata registry lets a validator declare its own infrastructure (provider, ASN, region) in additionalInfo.infrastructure. Declared fields are not self-verifying. This feed publishes the observed side: ProofLines independently derives provider/ASN/region for every validator in the active set from the network, so a consumer can compare declared against measured and flag drift. It is a separate, off-chain input. The registry stays the source of the declared record; this feed never writes on-chain.
proofline.observed.v1A JSON object with a metadata header and a validators array. Each entry is one ObservedInfra record.
| field | type | meaning |
|---|---|---|
validatorId | number | Stable on-chain validator id (the join key; survives active-set rotation). |
provider | string|null | Hosting provider / network operator (human label). Vantage-invariant. |
asn | number|null | Autonomous System Number of the validator's gossip endpoint. The deterministic anchor for comparison. |
region | string|null | Coarse IP-geolocation (ISO country code). |
active | boolean | In the consensus active set this epoch. |
lastObservedEpoch | number | Epoch of the most recent observation for this validator. |
staleEpochs | number | Epochs since last observed. 0 = observed this epoch. |
confidence | number | 0..1. Starts at ~0.9 for a clean ASN resolution and decays with staleness. |
observedAt | string | ISO-8601 timestamp of the observation. |
source | string | Always "proofline" here, so multiple sources can be merged and labelled. |
currentEpoch, generatedAt, activeSetSize, activeObserved, totalObserved, freshThisEpoch.
ProofLines runs a Monad node and reads the live gossip peer list (each validator's announced endpoint). For every validator id in the consensus set (resolved from the staking precompile), the announced IP is mapped to ASN, provider and country via a GeoIP database. provider, asn and region are IP-derived facts and therefore vantage-invariant: any honest observer resolves the same ASN for the same endpoint, so coverage matters more than the number of vantages. The feed publishes only what is observed; it does not ingest or echo any declared value.
The feed includes every active validator that exposes a gossip-observable endpoint, which is the large majority of the active set. A small, stable set of active validators announce no endpoint that propagates to independent observers (verified: independent vantages miss the same ids), so they cannot be measured by anyone. Those are simply absent from the feed and a consumer reads them as unverified. Nothing is guessed or backfilled: a value here means it was measured. The live activeObserved / activeSetSize counters in the header (and at the top of this page) report the true current coverage at any moment, so it is never overstated.
The active set is fluid (validators rotate in and out, and MIP-9 grows it over time). Records are keyed by the stable validatorId and persist across rotation: a validator that leaves the active set keeps its last observed infrastructure with active:false and a confidence that decays by staleEpochs, rather than disappearing. A validator never observed is simply absent, which a consumer reads as "unverified". The feed regenerates every epoch.
The schema field is a contract. proofline.observed.v1 field names and types are stable. Any breaking change ships as v2 at a new path (e.g. /monad/observed/v2.json); the v1 URL keeps its shape. The feed is served with Access-Control-Allow-Origin: * and Cache-Control: public, max-age=120, so browser apps can read it directly.
The feed plugs into the MRC-13 reference SDK as an ObservedSource. Sources can be combined so several independent vantages cross-confirm rather than any single map being trusted.
import { MetadataResolver, ObservedRegistry, ProofLineSource, MonadPulseSource } from "@shadowoftime/mrc13";
const resolver = new MetadataResolver({ rpcUrl, registryAddress });
const observed = new ObservedRegistry([new ProofLineSource(), new MonadPulseSource()]);
const v = await observed.verify(resolver, validatorId);
// v.infra.asn.status === "verified" when the declared asn matches what the vantages observe
// "disputed" when it differs, "unverified" when no source covers it
Or read it directly:
const feed = await (await fetch("https://prooflines.org/monad/observed/observed.json")).json();
const rec = feed.validators.find(v => v.validatorId === id);