Methodology

Every data source, every cache, every limitation — listed in plain text on this page. If a number on this site is wrong, this is where you find out why.

Verifiable is a commitment, not a claim of truth. Every number on this site is verifiable — we publish the method, expose the artifacts, and preserve the evidence. That is not the same as saying every number is right. "Verifiable" means we committed publicly to a specific value, using a stated method, with the raw material preserved, so if we are wrong you can catch us. Everything below is that audit trail — the check, not the claim.

When our own checks detect a value we now believe is wrong, we degrade the affected surface honestly — yellow badge, "data delayed" placeholder, scope note — rather than silently patch it. An auto-fixer that hides its own evidence would be the quietest liar of all.

Why this page exists. Most XRPL dashboards say "live" without telling you what that means. We commit to the opposite: every claim of "live" is backed by a measurable cadence below, and every cache is named with its TTL. If you disagree with our numbers, this page is the audit trail.

Scope note: the four-layer truth-audit (CLAIMS.yaml manifest + continuous L2/L3 alarms) covers metric-bearing pages. Form, policy, and help routes (/verify, /terms, /privacy, /security, /help/*) are self-attested — no data claims to audit.

Page last revised 2026-08-09. Versioned in git alongside the code that ships these features.

What's actually live, per surface

Three states: Live means a WebSocket pushes new data without a page refresh. Cached means we re-query the XRP Ledger on a fixed cadence and serve the most recent answer to all visitors during the cache window.

Page / surface State Cadence / TTL Underlying source
/api/ledger-tip Cached 60 sec server_info on public XRPL node
Ledger heartbeat chip
(every page header)
Cached polls every 30 sec → 20 sec server cache /api/ledger-tip
/wallet/<addr>
tx counter, last seen, sparkline today bin
Live WebSocket push (~3–5 sec per validated ledger) Browser subscribes directly to wss://xrplcluster.com
/wallet/<addr>
balances, holdings, network graph, AMM positions
Cached 5 min account_info, account_lines, account_tx, amm_info
/whales
large-XRP / tagged / trustset feed
Live WebSocket push (~3–5 sec per validated ledger) Browser subscribes directly to wss://xrplcluster.com; initial page load is from worker → Neon
/tokens, /token/<currency>/<issuer>
per-token swap activity
Live WebSocket push for new swaps; aggregates rebuild on next reload Browser WS to wss://xrplcluster.com; rollups from worker → Neon
/pools constellation
swap pulses + per-pool TVL
Live WebSocket push; TVL adjusts on each AMM LP-token delta Browser WS to wss://xrplcluster.com; static pool index seeded from amm_info
/api/pools/recent_events Live worker writes pool swaps as they happen; API reads on each call Worker → Neon Postgres
/cold-storage Cached 5 min account_info on labeled cold wallets
/rlusd
cross-chain supply, mint/burn events
Cached 5 min Ethereum public JSON-RPC (1rpc.io/eth) + XRPL public node (s1.ripple.com)
/api/rlusd/state Cached 5 min eth_call, eth_getLogs, gateway_balances, account_tx
/mpts
MPT registry + holder counts
Cached hourly snapshot worker daily ledger_data walk seeds the registry; hourly mpt_holders + ledger_entry refresh holder counts and outstanding supply
/mpt/<id>
detail page + supply history chart
Cached hourly snapshot; history chart from 90-day time-series same hourly worker; time-series written to mpt_supply_history on each successful walk
/mpt/issuer/<wallet>
issuer roll-up + attestation badge
Cached hourly snapshot aggregated from same hourly worker snapshot; domain attestation verified at index time against .well-known/xrp-ledger.toml
/lending
amendment status + protocol explainer
Cached 5 min feature RPC (amendment voting status); switches to live broker/vault data once LendingProtocol amendment activates

Five surfaces use browser-side WebSocket push (Live) from wss://xrplcluster.com. The remaining cached surfaces are derived from RPC calls or background snapshot workers — either there's no streaming equivalent, or the data changes on a cadence where a short TTL is the honest framing.

Data sources

XRP Ledger nodes

All on-chain data is read from public XRPL nodes — peers in the consensus network. A node can't lie about ledger contents without the rest of the network rejecting it.

Ethereum nodes

RLUSD is a stablecoin that lives on Ethereum as well as the XRP Ledger. To surface the Ethereum-side supply and treasury activity we query a public Ethereum JSON-RPC endpoint directly — no API key, no third-party gateway. The node only sees standard public reads (totalSupply, recent Transfer logs) and cannot lie about contract state without producing an inconsistent block that the rest of the Ethereum network would reject.

Disclosure. Ripple's documentation explicitly states their public servers are not intended for sustained business use. Browser-side traffic has now been migrated to the community-operated cluster wss://xrplcluster.com (run by the XRP Ledger Foundation), with wss://s2.ripple.com and wss://s1.ripple.com retained as automatic fallbacks. Our own rippled node is already in service for the forward-walker and MCP tool envelope reads (classified own-node in the source-tier table below); migrating the continuous XRPL streaming worker from wss://s2.ripple.com to the same own-node is the next step.

Database

Worker writes to Neon Postgres (managed Postgres, US-hosted) and a redundant local SQLite snapshot. The web app prefers Postgres for fresh reads and falls back to the SQLite snapshot if the Postgres connection is unavailable. No third-party analytics API (Bithomp, XRPSCAN, xrpl.to, XPMarket, DefiLlama, CoinGecko) feeds any of our metrics — price, volume, TVL, balances are all computed directly from the ledger.

Reference labels

Token display names are hand-curated in token_names.json in the public GitHub repo, each with a verifiable source URL (exchange page, project .toml, or audit), accepted via public PR review. Account labels are layered by priority:

  1. First-party hand-curatednamed_accounts.json in the repo; highest trust, manually reviewed.
  2. TOML-attested — issuers where the on-ledger Domain field and the domain's .well-known/xrp-ledger.toml form a verified two-way chain (see Domain attestation below). Stored with source=\'toml\'.
  3. Public reference lists — XRPSCAN well-known names; AMM pool and MPT issuer accounts we derive ourselves from on-chain state.
  4. Unlabeled — account address only; no label rendered rather than a guess.

Every label is stored with its source so users can tell first-party from TOML-attested from reference.

Signed integrity snapshots

Every UTC day we capture a small canonical set of headline metrics (validated ledger index, AMM pool count and total TVL, MPT count, named-accounts count), hash that capture into a Merkle leaf, extend a global append-only Merkle chain, and sign the resulting envelope with our Ed25519 private key. The signed JSON is published at /.well-known/snapshots/YYYY-MM-DD.json and the cumulative chain root at /.well-known/snapshots/chain.json. Both are static, cacheable, and independently verifiable forever.

Why this exists. Web dashboards can silently re-render history. A number that was true yesterday can quietly become a different number today, and visitors have no way to tell. Daily signed snapshots make that class of drift detectable: if the JSON you fetch today doesn't pass cryptographic verification against our published public key, something is wrong — either with the file, the chain, or the signing key. No other XRPL dashboard publishes a verifiable history of its own numbers.

Anatomy of one snapshot. Each daily file contains: the metrics block (name → value+unit pairs), a canonical-JSON SHA-256 leaf hash, the leaf's index in the global chain, the audit path proving inclusion under that day's chain root, the previous chain root, the new chain root, and an Ed25519 signature over the canonical envelope summary. All hashing follows RFC 6962-style domain separation — 0x00 byte prefix for leaves, 0x01 for internal nodes — so leaves and intermediate hashes can never collide.

How to verify yourself. Three independent checks, all server-side-free:

  1. Fetch our public key from /.well-known/snapshots/pubkey.pem, cross-check the fingerprint against this page, /about, and DNS TXT _xrpld-snapshot-key.xrpldashboard.com. All four sources must agree.
  2. Re-derive the leaf hash: SHA-256 of (0x00 || canonical-JSON of the {signing_domain, schema_version, snapshot_date_utc, metrics} subset).
  3. Walk the audit_path from the leaf, hashing each pair with the 0x01 domain separator. The result must equal the published chain_root.
  4. Verify the Ed25519 signature in signature_ed25519 (hex-encoded) against the canonical-JSON of the envelope summary {signing_domain, schema_version, snapshot_date_utc, leaf_hash, leaf_index, leaves_total, chain_root, previous_root}, using the public key from step 1.

Our one-click verification form runs all three checks server-side and shows each pass/fail independently — the same verification you can run locally with any Ed25519 library plus a SHA-256.

Trust model. A signed snapshot proves only that the published number existed on the recorded date and was signed by the key whose public half we pin in four places. It does not prove the number was correct — that depends on the source data and our collection logic, both documented in the rest of this page. Signed snapshots make it impossible for us to quietly rewrite history without being caught, but they are not a substitute for the rest of the methodology audit; they are the audit's lower bound.

Signing key fingerprint. 7F:D4:F2:F4:D2:57:7C:BE

Schema version: 3. Signing domain: xrpldashboard.com/signed_snapshot/v1. Curve: Ed25519. Browse the public snapshot index at /snapshots/.

On-ledger anchor of the snapshot chain

Since 2026-08-07, each daily chain_root is additionally committed inside an XRP Ledger Payment transaction memo. The anchor account rL2yMECEyUT94pLDrAcetMNMG1H4xqpNWQ sends the payment to the ops account rwrcJL3Exd1ZUYz11Wug6wvWC448CiTXfd. The transaction is 1 drop, plus the standard XRPL fee; the memo is the payload. This puts the chain root on a canonical, third-party-replicated timeline that we cannot rewrite without minting a public contradiction.

Cadence. Weekly, manual today. The language upgrades when automation lands. Missed weeks are visible on-ledger and will be reconciled explicitly rather than backdated.

First anchor. Transaction 01D0BB9D230955F43DB35703E2EB7F5DFA43CEB69CCBBF57FBC8F17407E50DF8, validated in ledger 106140698 at 2026-08-07 21:49:32 UTC (result tesSUCCESS).

Memo format (v1, normative). All payload lives inside the single MemoData field. Wallet UIs may auto-populate MemoType="Description" and MemoFormat="text/plain"; verifiers must ignore those and read only MemoData.

Verifier rules (v1, normative).

  1. Strip trailing whitespace from MemoData before splitting or comparing. Xaman and some other wallets append trailing newlines (observed: six \n bytes) that are not part of the payload.
  2. Identify anchor type by the leading token in MemoData (the string before the first |), not by MemoType.
  3. Enforce source-account identity: the payment MUST originate from the anchor account above.
  4. Enforce destination-account identity: the payment MUST be to the ops account above, except for a single sanctioned bootstrap-hop transaction (E94ADB8CF438EB94DCC00725572CBCC03ACC3084F12DE706AEB4D418B6A7438B) that funded the ops account before anchoring began.
  5. Enforce sequence continuity: correction anchors reference the prior_tx_hash they are replacing; there are no silent gaps.
  6. Compare chain_root_hex from the memo to the published chain_root for the same ISO date in /.well-known/snapshots/chain.json. Any mismatch is a live-alarm event.

Trust model — what the anchor proves and what it does not. The on-ledger anchor proves timestamped commitment to a published digest — that this exact chain_root existed and was published by this date. It does not prove the underlying analytics are correct; that is the job of the four-layer audit, cross-checks, and published methodology. The anchor makes silent rewriting detectable, not errors impossible. The signed-snapshot chain is tamper-evident against the site alone; the on-ledger anchor makes tampering additionally require a contradicting public XRPL history — a much larger and third-party-observed act.

Full spec (accounts, memo layout, deviation history, pre-lock amendments) lives at docs/ONLEDGER_ANCHOR_SPEC.md in the source repository.

Domain attestation

A "domain-attested" issuer is one we can cryptographically link to a public web domain by following a two-way chain. First, the issuer's on-ledger Domain field (set via AccountSet) points to a hostname (e.g. sivax.io). Second, that hostname publishes an xrp-ledger.toml file at /.well-known/xrp-ledger.toml whose [[ISSUERS]] block explicitly names the same on-ledger wallet. Both directions have to agree — if either side is missing or contradicts, no badge.

This matters because anyone can mint an MPT and claim to be an institutional issuer. Attestation lets a visitor verify the claim without trusting xrpldashboard: the domain is the badge, the .well-known/xrp-ledger.toml file is publicly readable, and the entire chain is independently checkable.

What the green ✓ does NOT mean: it is not an endorsement, not KYC, not legal or regulatory verification, not a signal of financial soundness. It only certifies that the on-ledger account and the named domain are operated by the same party. Whether that party is trustworthy in any other sense is for the visitor to research.

Where the badge renders: Currently on /mpt/issuer/<wallet> pages for verified MPT issuers. Will extend to /tokens, /whales, and homepage label surfaces as more issuers publish xrp-ledger.toml attestations.

RWA attribution

How institutional Real-World Asset families surface on /rwa — and what we deliberately exclude.

Worked example: detecting the rondo spoof

A wallet with vanity prefix rondo4tPzRyU72uNpY97iCqXGYd35AmYv set its on-ledger Domain field to ondo.finance/ousg. A one-way Domain check would have validated this as Ondo. The two-way TOML chain caught it:

  1. The on-ledger Domain field claims ondo.finance/ousg
  2. Fetching https://ondo.finance/.well-known/xrp-ledger.toml
  3. The [[ISSUERS]] block lists rHuiXXjHLpMP8ZE9sSQU5aADQVWDwv6h5p as canonical OUSG issuer — NOT the rondo vanity wallet
  4. Two-way chain: BROKEN. Vanity wallet is impersonating Ondo.

This same wallet also issues a fabricated ONDO token (Ondo's governance token is Ethereum-only — no XRPL presence) and pairs with the BlackRock and Franklin Templeton vanity wallets in self-referential spoof pools. After adding OUSG and ONDO to the verified-brand allowlist, all 12 spoof pool sides auto-flag with ⚠ on /pools.

Pools constellation — how comets are sized

The constellation on /pools fires a comet at the matching star whenever a top-10 AMM pool sees an on-chain event: cyan inbound for AMMDeposit, amber outbound for AMMWithdraw, white impact ring for a Payment swap routed through the pool. The comet's size carries one extra dimension — the magnitude of XRP that moved on the XRP side of the pool in that transaction.

The XRP-side delta is extracted from the transaction's meta.AffectedNodes block by the XRPL streaming worker (xrpl_stream.py) at write time — never re-derived client-side. For IOU↔IOU swaps and pre-walker rows where no XRP delta is available, magnitude is recorded as NULL and the comet renders at neutral scale (1.0×) — honest "unknown", not a guessed value. Walker began writing magnitudes on 2026-06-10 at 12:21 UTC; events before that bootstrap timestamp will always render neutral.

Scaling math. Sublinear log scale on the XRP side, calibrated against live percentile data so dust transfers stay perceptible without flooring everything to a single value:

scale = clamp(0.7, 0.7 + 0.25 · log₁₀(xrp · 100 + 1), 2.0)

Anchor points against the live distribution: dust (1 drop) → 0.70× floor; p50 (0.10 XRP) → 0.96×; p95 (46 XRP) → 1.62×; p99 (259 XRP) → 1.80×; max observed (3,641 XRP) → 2.0× cap.

Under prefers-reduced-motion the comets are suppressed entirely; the constellation status pill flashes the event label (DEPOSIT / WITHDRAW / SWAP) in the matching colour for 250 ms instead. Information is delivered, motion is not — same accessibility contract as the homepage globe.

Sidechain bridge — how the verifier set is read

/sidechain renders one row from bridge_signer_history — the most recent observed state of the SignerList on the Axelar XRPL Gateway account rfmS3zqrQrka8wVyhXifEeyTwe8AMz2Yhw. The hero stat is computed from that row at request time: when every signer carries the same weight the page shows M-of-N (quorum ÷ uniform weight = the count that must agree); when weights diverge it shows the raw quorum and signer count instead, because a flat M-of-N would mislead.

The walker (bridge_signer_walker.py) runs hourly via launchd. On its first run it took an account_objects snapshot of the live SignerList and wrote one row with tx_hash = 'BOOTSTRAP' — that row anchors the rotation timeline but does not itself count as a rotation. From there each pass scans account_tx on the gateway in the validated-ledger range, filters for SignerListSet transactions with tesSUCCESS, and inserts one row per observed rotation. The PK on (ledger_index, tx_hash) makes overlapping re-runs idempotent.

Bootstrap horizon: 2026-06-08 11:07 UTC. Any verifier rotation that happened before that ledger is not in our timeline — same shape as the pools-constellation magnitude horizon above. The freshness pill on the page is driven by the walker's last successful run, not by the row's close_time: rotations are rare, so a "data fresh 1 hour ago" badge means the walker checked the chain an hour ago and found no new rotation — not that the multisig itself changed. Cross-references to axelarscan.io and bithomp's gateway view are linked on the page for anyone who wants to verify against the canonical XRPL ledger directly.

Methodology notes

Known limitations

/analytics bot filter

The /analytics page renders the same visitor counts twice: filtered ("humans") and unfiltered ("bots"). Both series come from page_views rows; the filter is a query-time predicate, not a stored label, so the rule is transparent and reversible.

Rule. A row is classified as bot if its (path, user-agent) pair has appeared ≥ 30 times in the trailing 7 days AND the total hit count is ≤ 1.10 × distinct visitor identifiers for that pair. The volume threshold isolates aggregate scanner activity from ambient traffic; the ratio clause distinguishes rotating-IP proxies (many one-shot visitors) from viral human bursts arriving from a shared link (also many one-shot visitors, but usually staying below the volume floor).

Retrospective flip. Because the rule reads the current 7-day aggregate on every query, rows can reclassify as evidence accrues. A burst in progress is counted as human for its first ~29 hits, then flips to bot the moment the 30th hit lands. Recent counts converge on the honest answer rather than committing to an early classification.

Declared boundary. The rule keys on repeated (path, user-agent) volume. A scanner rotating user-agent strings as well as IPs — one hit per (ip, ua) cell — keeps every cell below the volume floor and is not caught by this rule. We accept the boundary at current traffic (the arms-race cost isn't worth the accuracy gain); if scanner behaviour shifts, the rule will be extended and this section updated.

Burst-cohort classifier

A third rule catches rotating-IP fleet attacks where each IP hits a real page exactly once with a stock browser user-agent — invisible to both prior rules.

Signal. A spike in unique IPs for a (country, path) pair far above that combination's 30-day baseline. Example: Israel + /whales averaging ~2 unique IPs/day spiked to 774 unique IPs in one day — a 387× multiplier. The rule fires when unique IPs exceed 10× the trailing-30-day median AND exceed an absolute floor of 50 unique IPs (prevents small-baseline countries from triggering on noise).

Scope. Classification is by cohort (country + path + day), not by individual history. All visits matching the cohort fingerprint on a burst day are reclassified as bot — including any genuine visitor from that country who happened to visit that path on that day. The rule is retroactive: once a burst day is confirmed, historical rows reclassify automatically the next time /analytics is queried.

Declared boundary. A genuine visitor from Israel who visited /whales on a burst day is retrospectively classified as bot. We accept this false-positive at current traffic scale — on a day with 774 IL/whales hits, the probability that any of them were genuine non-fleet visitors is low, and growing with evidence. The rule distinguishes on the COHORT shape (deceptive UAs, real pages, one hit per IP, country-concentrated) rather than on being a bot — declared AI crawlers identify themselves in their user-agent and are classified by the existing UA pattern rule before this predicate is checked.

Review trigger. Re-audit thresholds when daily human traffic exceeds 1,000 unique visitors. At that scale, genuine single-visit /whales traffic from any country becomes frequent enough that the absolute-floor and multiplier math needs rechecking against the false-positive rate.

Infrastructure

MCP tool envelope sources

Every tool served by the MCP server (see Agent Tier design doc) carries a methodology_url field in its proof envelope. The anchors below are the deep-link targets those envelopes cite — each names the data source, freshness contract, and any known limitations for one class of tool. If a tool's envelope points here, this is the section a caller lands on.

Ledger primitives (get_ledger_tip)

Source: local_rippled (self-hosted node) or s1.ripple.com:51234 when the local node is unavailable. Returns the current validated ledger index, close time, and hash — the same values the browser liveness chip reads. Freshness contract: ≤ 5s (ledger close cadence). No cache — every call is a live ledger RPC. Envelopes emit as_of as the ledger close time, not the fetch time.

Amendments (get_amendments_status)

Source: s1.ripple.com:51234 via the feature RPC + the canonical Amendments ledger object. Amendment definitions the responding node doesn't recognize are named via off-ledger sources in amendments_state.KNOWN_UNRECOGNIZED_HASHES (each entry cites a verifiable rippled PR or spec URL — see the /amendments page for the human surface, including a truth-first note whenever the node lags a network-enabled amendment). Freshness contract: ≤ 5min (per amendments_state.CACHE_TTL). cross_check_status = agree when the enabled-count from the feature RPC and the Amendments ledger object are within tolerance; disagree otherwise.

Amendment vote tallies — honest limit. The feature RPC's count and threshold fields report the responding node's own validator vote — they return None for any non-validator, which includes our self-hosted node and every public gateway (s1/s2). Aggregated trusted-validator support percentages therefore cannot be sourced from a single rippled RPC. During active-news windows for specific amendments we attach an interim honest label sourced from xrpscan.com's amendments API (curated in amendments_state.INTERIM_VOTE_NOTES, refreshed manually against the primary source). A live-fetch integration with the honest_partial envelope pattern is in design (owed 2026-08-22).

Validator UNL (get_unl_status)

Source: vl.ripple.com (Ripple's canonical published Unique Node List) cross-checked against vl.xrplf.org (XRPLF-published UNL). Returns the list version, ephemeral key expiration, and validator entries. Freshness contract: ≤ 30min. When one of the two published lists fails to fetch, the tool routes honest_partial=true with a scope_note naming which list failed — never fabricates a synthetic UNL from a single source.

Whale events (get_whale_events, get_whale_watchlist)

Source: local_rippled_stream_capture — the same live capture feed the /whales page reads. Two-tier threshold, both named in the response payload: xrpl_stream.py captures at ≥ 50_000 XRP (walker floor, env-tunable via WHALE_XRP_THRESHOLD_XRP); the MCP tool + /whales default display floor is ≥ 100_000 XRP (mirrors the human surface). Tagged rows use TAGGED_XRP_FLOOR_DROPS = 100 XRP. TrustSet entries are excluded — they signal intent, not movement. Freshness contract: ≤ 5min (stream cadence + PG-write lag). Token-denominated tagged rows bypass the XRP floor; agents needing USD-priced sizing must join against the token-price oracle themselves. The tool response includes this caveat verbatim in a note field.

RLUSD supply and 24h flow (get_rlusd_supply, get_rlusd_flow_24h)

Supply. Source: ethereum_public_rpc + xrpl_public_rpc — the same rlusd_live cache the /rlusd page reads. Freshness contract: ≤ 30min. When either chain fetch errored, the envelope emits honest_partial=true with a scope_note naming which side failed — never a fabricated cross-chain total. cross_check_status='not_applicable' here: the two chains measure independent issuance pools, not the same fact from two sources, so they cannot agree/disagree.

24h flow. Source: rlusd_supply_history + xrpl_gateway_balances. Freshness contract: finalized_only — the machine-readable form of the R1/R2 finalized-window rule (snapshot_date < today_utc). Today's partial-day row is invisible to this tool; only rows the answer_plausibility_walker would have evaluated as finalized are eligible. This is the fix for the RLUSD 53-day false-flat class of bug that led to the R2 patch (see docs/TRUTH_AUDIT_DESIGN.md).
cross_check_status here is derived from a real paired computation: stored xrpl_net_change_24h (Option-A gateway_balances diff) vs derived xrpl_supply[t] − xrpl_supply[t-1]. Agreement within CROSS_CHECK_EPSILON_RLUSD (1) emits agree; any larger divergence is disagree — an agent reading disagree should downweight the number, not just relay it. The same three-state verdict is surfaced as a chip on the /rlusd page next to the freshness pill — one derivation, two surfaces.

AMM pools (get_amm_pool, get_amm_top_by_tvl)

Source: rank_amms_walker + amm_tvl_recorder — the same amm_ranked_pools snapshot the /pools page reads. rank_amms curates the ranked set (thresholds on liquidity and non-zero pair sides); amm_tvl_recorder fills the tvl_usd + tvl_status columns from live pricing. Rows with NULL tvl_usd sort last in the top-N view — the absence is preserved, not silently dropped. Freshness contract: ≤ 30min — matches the recorder cadence. Unranked AMM accounts (below thresholds, or newer than the last snapshot cycle) return a RuntimeError from get_amm_pool rather than a stub — absence IS the signal, same rule the Day 3 finalized-window tool applies.

Token attestation (get_token_attestation)

Source: verify_toml_accounts + enrich_token_names — the same account_labels table the /tokens per-row attestation badge reads. Three-state ladder mirrors app.py:2589-2598 (v3 §7): verified — the issuer address has a label with source='toml' (verify_toml_accounts successfully fetched and parsed the issuer's .toml declaration); self-described — labeled with a non-toml source (derived:xls-15, enrich_token_names): named by an evidence trail but not TOML-verified; null — no account_labels row for the issuer. Absence IS the signal — the field is explicitly null, never fabricated. Freshness contract: daily. Every response includes dispute_contact_url in the data payload — first-party channel for any issuer who disagrees with the label. Same target the /tokens per-row footer link uses (purpose=attestation-dispute in app.py CONTACT_PURPOSES).

RWA families and pools (get_rwa_families, get_rwa_pools)

Source: rwa_family + rwa_pool_attribution — the same curated allowlist tables the /rwa page reads. get_rwa_families returns every family with its attributed pool count; get_rwa_pools returns every pool-attribution row with provenance + tvl_usd joined against amm_ranked_pools (same join as app.py:3086).
attestation_level{verified, inferred, preliminary} is a per-family curation grade (schema constraint in db.py:365); confidence{high, medium, low} is a per-pool attribution grade. Both are curated, not derived — the manifest of what we chose to surface, and with what strength of claim. Freshness contract: daily (families) / ≤ 30min (pools, since the TVL join reads the amm_ranked_pools snapshot). Both responses include dispute_contact_url — a family or pool owner disagreeing with an attribution has the same first-party correction channel.

MPT snapshot (get_mpt_snapshot)

Source: mpt_snapshot + mpt_holders_refresh — the same mpt_snapshot JSON payload the /mpts page reads. Reports total active MPTs and a by-class breakdown as the walker recorded them; not third-party-naming (no per-issuer labels here, so no dispute_contact_url). A mislabel of a specific MPT issuer lands on get_token_attestation, not here. Freshness contract: daily (mpt_snapshot walker cadence). The snapshot dict includes snapshot_age_seconds — an agent that wants a fresher read can compare that value against the daily contract and back off accordingly.

Signed snapshot (get_signed_snapshot, verify_snapshot_signature)

Source: signed_snapshot_walker — the daily launchd job that writes signed_snapshots/YYYY-MM-DD.json with an Ed25519 signature, RFC 6962-style Merkle audit path, and pinned pubkey fingerprint. These two tools are the moat expression as an MCP surface: get_signed_snapshot(date_str) retrieves the full signed envelope for one ISO date (raises when absent — the walker has not produced this date yet, or the date predates the chain start). verify_snapshot_signature(envelope) is stateless: an agent hands back the exact envelope it received (either the full {data, proof, server} wrapper or the bare signed payload) and this tool re-derives the leaf hash, checks the Ed25519 signature against the pinned pubkey, and checks the audit path against the claimed chain root — the same verification path a third-party verifier would run with no shared state. Freshness contract: daily (get) / ≤ 5min (verify — stateless in-process check). The round-trip receipt: a caller who screenshots a metric today can hand the snapshot back into verify_snapshot_signature six months later — from a completely different site, or from a local Python REPL using the same signed_snapshot.verify_envelope function — and prove the number wasn't silently changed. See Signed integrity snapshots above for the human-facing deep-dive on the chain design.

For AI agents

This site is a public read-only source of XRP Ledger and cross-chain data. Automated retrieval, citation, and machine reading are welcome — anticipated, even. This section is the transparent contract for anything reading this page programmatically.

Discovery surfaces

Use this data — copy-pasteable snippets

Four self-contained snippets — curl, Python, JavaScript for fetching a claim envelope + a Python end-to-end verifier for the daily signed snapshots — live on the claims manifest: /claims#use-this-data. Every snippet on that page was executed against live prod before shipping. Copy any one, run it, get a proof-annotated response back in under a second.

Proof-annotation contract

Every JSON payload from the MCP server carries a receipts block. The MCP server is public at https://mcp.xrpldashboard.com/mcp (public beta through 2026-09), streamable-http transport, protocol version 2025-06-18, no auth. Backed by our own rippled node on the Lenovo box. The read-only HTTP API will carry the same block when it ships. Agents verify our numbers locally — against ledger truth, against the signed-snapshot chain, against CLAIMS.yaml — without a support ticket. Envelope shape:

{
  "source":              "local_rippled | neon_postgres | ethereum_1rpc | ...",
  "as_of":               "2026-07-29T15:42:00Z",
  "methodology_url":     "https://xrpldashboard.com/methodology#...",
  "claims_ref":          "rlusd_xrpl_supply",            // where a CLAIMS.yaml entry exists
  "snapshot_signature":  "ed25519:...@chain#N"           // where the datum is snapshot-derived
}

The read-only OpenAPI spec is live at /openapi.json (Swagger UI at /docs) — the ProofAnnotationEnvelope schema above is machine-readable at #/components/schemas/ProofAnnotationEnvelope, and the 15-tool MCP inventory is at info.x-mcp-tools. The MCP server is public and live at https://mcp.xrpldashboard.com/mcp (public beta through 2026-09; the server runs as a systemd daemon on the Lenovo box, same host as our rippled node, exposed via Cloudflare Tunnel). Per-tool freshness contracts are declared inside each tool response envelope (freshness_contract field: ≤ 5min / ≤ 30min / daily / finalized_only) and mirror the on-page freshness chips agents see for the same data. See the design doc on GitHub for the full build sequence.

NFT data sources — sovereign vs. third-party

The /nfts page surfaces XLS-20 NFT activity (mints, burns, offers, sales) with two source classes, each labeled at point of display:

The 2026-08-10 gap audit of the historical range produced ~95.6% coverage of a 2,958,523-ledger range (2026-04-01 → head): 2,194,433 ledgers were observed with NFT activity (4,955,611 events), ~635,090 contained no NFT transactions (legitimately empty of NFT tx), and ~129,000 are residual holes — public-Clio 503 responses the walker persisted past during backfill. The 95.6% denominator is the full range; empty-by-consensus ledgers count as covered, residual holes count as missed. This is disclosed on /nfts rather than surfaced as false-complete.

Under our sovereign-source doctrine — SELLABLE_REQUIRES_SOVEREIGN_SOURCE — any endpoint whose underlying data was read through a third-party pipe stays free forever, honestly labeled, until it can be re-derived from our own infrastructure. NFT historical backfill therefore sits permanently in the free tier until (and unless) we buy a full-history rippled node and re-derive the range from it. The doctrine is fully documented in docs/X402_RAILS_DARK_SCOPING.md § 2 Paid-candidate filter.

Signed-snapshot verification

A tamper-evident chain of daily Ed25519-signed database snapshots is served under /.well-known/snapshots/chain.json. The public key is pinned at /.well-known/snapshots/pubkey.pem. Agents that need audit-grade evidence can verify any snapshot payload cryptographically against this key without trusting the server or the network path in between. See Signed integrity snapshots above for details.

Since 2026-08-07, each daily chain_root is additionally anchored on the XRP Ledger from account rL2yMECEyUT94pLDrAcetMNMG1H4xqpNWQ (first anchor tx 01D0BB9D230955F43DB35703E2EB7F5DFA43CEB69CCBBF57FBC8F17407E50DF8, ledger 106140698). Cadence is weekly, manual today. Memo format v1 (namespace-in-MemoData) and verifier rules — including the mandatory .strip() on MemoData before splitting — are documented at On-ledger anchor of the snapshot chain above.

Rate limits

Free for humans and identified agents at reasonable volume. Transparent, no silent throttling. Declared shape:

Enforcement is live as of 2026-08-02. The numbers here, in /.well-known/agents.json, and what the server actually enforces are held in sync by a regression test that reads the module's default-rate strings on every run.

Retention and privacy

We do not retain agent queries. Aggregate demand telemetry (tool-call counts per tool per day, unique anonymous client fingerprints, MCP-server session durations) is kept to inform which surfaces to prioritise. No query bodies, no client identifiers beyond what an anonymous fingerprint provides.

Why this is free

The Agent Tier ships free at v1 by construction — no accounts, no API keys, no payment rails. A paid tier is only a decision on the table after (1) US legal guidance on receiving crypto micropayments as a solo LLC clears, and (2) 60-90 days of observed demand justify the metering. Until then, everything here stays free, and the four-zeros posture (nothing here receives crypto, holds fiat, or touches user funds) is preserved by architecture, not by promise. See the Agent Tier design doc for the full scope contract.

Freshness contract for this section and the agent-tier surfaces (llms.txt, agents.json, openapi.json): last verified 2026-08-29. Bumped whenever the agent-tier surface changes.

Versioning this page

This methodology page is part of the same git repository as the code. Any time the site's caching behavior, data sources, or methodology changes, the corresponding edit lands in this file in the same commit. The commit history of templates/methodology.html is the canonical change log.

Found a discrepancy? Open an issue on GitHub or send us a note. We'll either fix the data, fix the explanation, or — if it's a genuine ambiguity — add it to the limitations list above.