monerometrics

MCP serverMonitoring & ops

monerometrics gives your AI live visibility into the Monero (XMR) network. Once added, your AI can report on blockchain reorgs, orphan blocks, and how concentrated mining power is across pools, so you can check network health just by asking.

Use monerometrics in Claude, ChatGPT or Ahel Desktop

Free. Sign in, add monerometrics and connect your AI. About a minute.

Also: Claude Code · Cursor · Codex

Then ask your AI: use the network info tool from monerometrics

Details

Available today. Use it from your connected AI after setup.

Add monerometrics, then ask your AI about the Monero network, for example recent reorgs or how mining power is distributed across pools.

monerometricsStart free

What your AI can do with it

  • Report live Monero network metrics
  • Track blockchain reorgs as they happen
  • Identify orphan blocks on the Monero chain
  • Show how mining power is concentrated across pools
  • Answer questions about current Monero network conditions

Install monerometrics

The server’s own address, for the clients that take one directly. Or connect Ahel once and every client you use reads it from one address, with the account kept on Ahel rather than in each client’s config.

  • Claude Code

    claude mcp add --transport http --scope user monerometrics 'https://api.monerometrics.net/mcp'

    Run it once in your project, then open /mcp to approve any sign-in the server asks for.

  • Claude Desktop

    https://api.monerometrics.net/mcp

    Add a custom connector in Settings, paste this address, and approve the sign-in.

  • Cursor

    cursor://anysphere.cursor-deeplink/mcp/install?name=monerometrics&config=eyJ1cmwiOiJodHRwczovL2FwaS5tb25lcm9tZXRyaWNzLm5ldC9tY3AifQ==

    Open the link and Cursor adds the server at that address.

  • ChatGPT

    https://api.monerometrics.net/mcp

    In Settings, enable Developer mode, create an MCP app, and paste this address. Your plan and workspace must allow custom apps.

  • Codex

    codex mcp add monerometrics --url 'https://api.monerometrics.net/mcp'

    Run it once, then sign in with codex mcp login monerometrics if the server asks for an account.

From the project's README

As published by nowi333/monerometrics in README.md.

A reorg-aware observatory for the health of the Monero network: public dashboard + API.

monerometrics is community-funded. A Kuno campaign funds the next two years: hosting, FCMP++ readiness, free reorg alerts, open data export and a second node.

monerometrics measures and historizes the health of the Monero network: network hashrate, block time, mempool state, mining-pool distribution and, above all, chain reorganizations (reorgs) and orphan blocks, which most block explorers surface poorly.

The project was born from the August 2025 Qubic episode, during which a mining pool paying miners in its own token approached a majority of the network hashrate, then withheld blocks and released a longer private chain. On 14 September 2025 that produced an 18-block reorganization at height 3,499,659 that erased about 36 minutes of history, well past the 10 confirmations everyone treated as final: transactions thought settled were suddenly unconfirmed again. The public debate lacked reliable, accessible data to settle it. monerometrics fills that gap with a neutral, verifiable, reorg-aware observatory.

It is open-source and self-funded, with no ads and no tracking. The dashboard and the API are free and open, permanently: there is no paid tier, and there will not be one.

From a diploma project to a community tool. monerometrics V1 was built and defended as a French professional IT-infrastructure project, and it earned the diploma. With that chapter closed, the goal of V2 is to hand the project over to the Monero community.

  • Dashboard: monerometrics.net
  • Public API: api.monerometrics.net (OpenAPI documented)
  • MCP server: https://api.monerometrics.net/mcp (Streamable HTTP; in the MCP Registry)
  • Tor hidden service: 6wbhchvavey26lbtscl6w6qg76balycixtsklcggrsslyk4xah6sbbad.onion (dashboard + API, no Cloudflare, no IP exposure, see Access over Tor)

What it does

Dashboard, a React SPA in four sections, available in English, French and Spanish, light and dark themes, readable on a phone:

  • Consensus and reorganizations: a chain-fork visualizer that draws competing branches as a tree, reorg statistics by window up to everything recorded, and orphan history. Each block names its pool and how the attribution was established, and merge-mined blocks are flagged.
  • Mining concentration: pool distribution, the combined share of the two largest pools, and the evidence behind each attribution.
  • Network state: hashrate, block-time variance, mempool, emission, and a transaction fee estimator reading the node's four priority tiers live.
  • Peer-to-peer market: Haveno premium over spot on both sides of the book, live order-book depth, resting liquidity, and premium by payment method.
  • Transaction search: paste a transaction hash and get what Monero actually makes public about it, plus which pool mined its block and whether that height was ever contested by a reorg. The hash travels in a POST body, so it never lands in a URL.
  • Every time series is navigable: a range strip under each chart, drag to move through time, zoom, and statistics that follow the visible window rather than the whole series.

Public API: FastAPI, read-only JSON endpoints grouped by theme, documented via OpenAPI.

Reorg detection: a Python worker reads each block from a synced node, computes the indicators and detects reorganizations by re-checking a rolling window of recent blocks against the node, recording their real depth and the transactions they displaced.

Architecture

A visitor reaches the dashboard or API through Cloudflare, which terminates TLS and applies its edge protections, then forwards to a hardened edge server (nginx + ModSecurity WAF). The edge serves the static dashboard and reverse-proxies the API to the k3s node, where the application core runs: a Monero node, the indexer, PostgreSQL and the API.

A second, independent path exists: a Tor hidden service running on the edge, which bypasses Cloudflare entirely and serves both the dashboard and the API on a single origin, behind the same WAF.

flowchart LR
    User(["Visitor"]) -->|HTTPS| CF["Cloudflare<br/>DNS · WAF · proxy"]
    CF -->|"HTTP/2 origin pull"| Edge
    Tor(["Tor visitor"]) -.->|"Tor network"| Onion
    subgraph HZ["Hetzner Cloud · private network 10.0.0.0/24"]
        Onion["tor daemon<br/>hidden service v3"]
        Edge["edge 10.0.0.20<br/>nginx + ModSecurity WAF<br/>static dashboard"]
        Onion -.->|"127.0.0.1:8080"| Edge
        subgraph K3S["k3s 10.0.0.30 · no public ingress"]
            API["FastAPI"]
            Worker["Python worker<br/>(indexer)"]
            DB[("PostgreSQL 17")]
            Node["monerod 0.18<br/>pruned"]
        end
        Edge -->|"api. proxy_pass"| API
        API --> DB
        Worker --> DB
        Worker -->|JSON-RPC| Node
    end
    Node <-->|"P2P sync"| Monero(["Monero network"])
    API -.->|"XMR price (cached)"| Price(["CoinGecko · Kraken<br/>Haveno (RetoSwap)"])

Access over Tor

The dashboard is also published as a Tor v3 hidden service, giving a path to the data that does not depend on Cloudflare and does not expose the visitor's IP address:

6wbhchvavey26lbtscl6w6qg76balycixtsklcggrsslyk4xah6sbbad.onion
  • One origin, no leaks. The hidden service serves the dashboard on / and reverse-proxies the API under /api. The SPA detects an .onion host at runtime and switches to that relative path, so no browser request ever leaves the hidden service for the clearnet. One build serves both.
  • No TLS, on purpose. Tor already encrypts and authenticates end to end, and the .onion address is the service's public key.
  • Same WAF. The hidden-service vhost runs the same ModSecurity rules as the clearnet site, so Tor is not a way around the filtering.
  • Not reachable from the internet. The .onion vhost listens on 127.0.0.1:8080 only, so the Tor daemon is the sole thing that can reach it. Tor makes outbound connections only.
  • No logs. Every request arrives from 127.0.0.1, so access logging on this vhost would record nothing useful and is disabled.
  • Discoverable. The clearnet site advertises it with an Onion-Location header.
  • Rate limiting. Per-IP limiting is meaningless over Tor, so hidden-service traffic is tagged by nginx and isolated in its own bucket with a higher ceiling. The tagging header is stripped on the clearnet vhosts, so it cannot be forged from the internet.

Deployed by the tor Ansible role (config/ansible/roles/tor/).

Infrastructure

The platform runs on Hetzner Cloud (Nuremberg) as three Ubuntu 24.04 servers on a private network, each behind its own firewall. Administration goes over a Tailscale (WireGuard) mesh; Grafana is reachable over Tailscale only. Backups are paused: the tooling (Restic, encrypted, see k8s/monerometrics/BACKUP-PRA.md) stays in the repository, but no scheduled backup runs today.

flowchart TB
    Admin(["Administrator"]) -->|"SSH 22"| Bastion
    Internet(["Internet"]) -->|"80 / 443"| Edge
    subgraph HZ["Hetzner Cloud (nbg1) · 10.0.0.0/24"]
        Bastion["bastion 10.0.0.10<br/>firewall: SSH from admin IP only"]
        Edge["edge 10.0.0.20<br/>firewall: 80/443 from internet"]
        K3s["k3s 10.0.0.30 + data volume<br/>firewall: no inbound"]
        Bastion -. ProxyJump .-> Edge
        Bastion -. ProxyJump .-> K3s
    end
    Mesh["Tailscale mesh<br/>(admin · Grafana)"]
    Bastion --- Mesh
    Edge --- Mesh
    K3s --- Mesh
ServerTypePublic exposureRole
bastionCX23SSH from admin IP onlySole SSH entry point, ProxyJump to the others
edgeCX2380/443 from the internetnginx reverse proxy + ModSecurity WAF, serves the dashboard
k3sCX33 + 128 GB volumenone (outbound only)k3s cluster: monerod, worker, PostgreSQL, API, OpenBao

Key choices: per-server firewalls, a single SSH entry point, a WAF on the only public web surface, a zero-trust admin mesh, and a k3s node with no inbound exposure at all. Both hops run HTTP/2 with the WAF active. Everything is Infrastructure-as-Code: Hetzner and Cloudflare resources via Terraform, server configuration and CIS-aligned hardening via Ansible, images published to GHCR. Supervision with Prometheus + Grafana, with alert rules on indexing, node sync, disk and failing workloads. The backup tooling (Restic) is kept but paused.

Secrets. The cluster is provisioned with OpenBao (a free fork of Vault) and the manifests carry no plaintext credential. OpenBao is currently sealed and the workloads read their database credentials from a Kubernetes Secret (postgres-credentials-fallback) instead. Unsealing is a manual step after any restart, the trade-off of a single node; the fallback keeps the service up.

Running cost

From the Hetzner invoice, per hour of use, excluding VAT:

ItemUnit priceMonthly (730 h)
2 × CX23 (bastion, edge)0.0088 €/h12.85 €
1 × CX33 (k3s)0.0136 €/h9.93 €
3 × primary IPv40.0008 €/h1.75 €
128 GB volume0.0572 €/GB-month7.32 €
Total31.85 € excl. VAT · 38.22 € incl. VAT

Cloudflare, Let's Encrypt, Tailscale and GitHub Actions are on free tiers.

The volume is the item that moves, and the chain drives it, not indexing. The pruned Monero blockchain takes 106 GB; the entire indexed database, 3.77 M blocks and every time series, takes 3.1 GB, about 540 bytes per block, and grows by roughly 150 MB a year.

Growth is measured from the indexed data rather than estimated. Summing the recorded size of every canonical block gives 186.8 GB for the full chain, so this pruned node keeps a bit over half of Monero. The chain grew 22.18 GB over the last twelve full months, which is 12.35 GB a year on disk, steady across years (17.5 GB in 2022, 18.3 in 2023, 29.1 in 2024, 22.2 in 2025). At 94 % full, the 128 GB volume has about seven months left, so the plan is to take it to 160 GB (+1.83 €/month) and no further, since the same measurement says larger would sit unused.

How the indexer works

The worker (apps/worker/indexer.py) is the heart of the project. Every POLL_INTERVAL seconds it asks monerod for its state and, when the node is synced, runs three passes:

  1. Confirmation-window rescan (reorg detection). It re-fetches the headers of the last CONFIRMATION_WINDOW blocks (default 60) in one call and compares each hash to the canonical hash already stored. Any mismatch is a reorganization: the stored block is flagged orphan (is_canonical = false), the node's block becomes canonical, and a row is written to reorgs_detected with the real depth and the affected transaction count. This pass is what makes reorg detection work at all: forward-only indexing never revisits the past, so it would silently miss every reorg that rewrites already-indexed heights.

    The window is also the detection ceiling. A reorganization deeper than CONFIRMATION_WINDOW rewrites heights the rescan never looks at, so it would be missed entirely rather than reported with a wrong depth. At 60 blocks the margin is wide, the deepest Monero reorg on record was 18 blocks, but the limit is structural and the counts published here are complete only up to that depth. Raising it costs one larger header call per poll.

    The transaction count is num_txes, which excludes the coinbase, so it counts transactions users actually broadcast rather than the miner's own output.

  2. Forward indexing. It fetches the blocks above the last indexed height. Close to the tip it pulls full blocks one by one for accurate attribution; far behind (fresh deploy) it switches to a fast header backfill (~1000 blocks per call), enough for the network-health series and fast enough to fill the long windows in under two hours instead of never.

  3. Alternative chains (orphans). Most competing blocks are settled within seconds, long before the next poll, so the rescan above never sees them: it only catches a block we had already indexed. The worker therefore also reads the node's own alternative chains (get_alternate_chains, on an RPC port only the worker can reach) and records every competing block as an orphan. Before this pass, about one orphan in four was recorded.

flowchart TB
    Start(["Every POLL_INTERVAL"]) --> Info["GET /get_info"]
    Info --> Sync{"node synced?"}
    Sync -->|no| Wait["log progress · sleep"]
    Sync -->|yes| Rescan["Rescan last N blocks<br/>get_block_headers_range"]
    Rescan --> Diff{"stored hash<br/>≠ node hash?"}
    Diff -->|yes| Reorg["mark old → orphan<br/>insert new canonical<br/>record reorg (depth, tx)"]
    Diff -->|no| Fwd
    Reorg --> Fwd["Forward index<br/>new blocks (batch)"]
    Fwd --> Alt["Read alternative chains<br/>record orphans"]
    Alt --> Metrics["update Prometheus metrics"]
    Metrics --> Start

Observability. The worker exposes Prometheus metrics on :9100/metrics (indexing lag, reorg counter, sync state, pool-index size, blocks proven by view key, attribution conflicts) and writes a heartbeat file consumed by a Kubernetes liveness probe. The heartbeat is written only after a successful pass, so a loop that keeps failing is restarted and raises the WorkerStalled alert.

Mining-pool attribution

Monero is private by design: a coinbase carries no pool name, and stealth addresses mean you cannot look up "who was paid". Attribution has to be established, never assumed.

monerometrics never infers a pool's share from its self-reported hashrate. It only counts blocks it can tie to a pool by evidence, and records which kind of evidence was used, per block, in blocks.pool_source.

1. View-key proof, cryptographic (viewkey_proof). Some pools publish their wallet primary address and secret view key (see blocks.p2pool.observer/proofs). A view key only reveals incoming transactions; it cannot spend. That is enough to prove ownership of a coinbase output, with no trust in anyone's API:

R          = transaction public key, parsed out of the coinbase tx_extra (tag 0x01)
derivation = 8 · a · R                      (a = the pool's secret view key)
P_expected = Hs(derivation ‖ varint(i)) · G + B    (B = the pool's public spend key)

If P_expected equals the output key at index i, that output pays the pool's wallet: a mathematical fact, independent of any pool API, and therefore immune to the reporting lag that makes fresh blocks look unattributed. Implemented from scratch in apps/worker/pool_proofs.py (ed25519 point arithmetic, Keccak-256, Monero base58), about 33 ms per block.

2. Pool block lists, cross-referenced (pool_api). Pools that publish no view key still publish the blocks they found. Those lists are aggregated every ~2 minutes into an index {block_hash → pool} and matched by block hash, so the claim is anchored to a real block. The weakness is latency, not correctness: a pool slow to publish leaves its own recent blocks looking unknown until it catches up. A re-attribution pass on every cycle fixes those retroactively.

3. Coinbase heuristic, structural (coinbase_heuristic). A coinbase paying many outputs at once is characteristic of P2Pool, which splits the reward on-chain. Last resort, labelled as a heuristic.

Guardrails

  • Keys are self-checked at startup. Each view key must prove a block that the pool's own API claims. A key that fails is dropped, so a stale or wrong key can never mislabel blocks.
  • Conflicts are surfaced, not hidden, and counted in monerometrics_attribution_conflicts_total.
  • Unproven claims are flagged. When a pool that publishes a view key lists a block that key does not prove, it is recorded as pool_api_unproven rather than shown with the same confidence.
  • Proof outranks APIs when both are available.
  • Source health is public at /pools/sources, so a silently failing source is visible instead of quietly inflating unknown.

Sources aggregated

PoolEndpointMethod / depthView-key proof
supportxmr.comwww.supportxmr.com/api/pool/blocks?limit= (up to 10000)✅
hashvault.proapi.hashvault.pro/v3/monero/pool/blocks?limit=&page=0 (up to 10000)✅
moneroocean.streamapi.moneroocean.stream/pool/blocks?limit=100 (pool cap)✅
xmrpool.euweb.xmrpool.eu:8119/get_blockspaginated by ?height=✅
ownblock.xyzno block APIview key only✅
p2pool (main / mini / nano)*.p2pool.observer/api/pool/blocks?limit=·
nanopool.orgapi.nanopool.org/v1/xmr/pool/blocks/0/{n}path count (~4600)·
c3pool.comapi.c3pool.org/pool/blocks?limit= (up to 10000)·
kryptex.compool.kryptex.com/xmr/api/v1/pool/blockspaginated via next·
herominers.commonero.herominers.com/api/get_blockspaginated by ?height=·
monerohash.commonerohash.com/api/get_blockspaginated by ?height=·

P2Pool runs three sidechains (main/mini/nano); all three are polled and collapsed into a single p2pool label, since they are one decentralised network from a centralisation standpoint.

What stays unattributable, and why

About a fifth of blocks end up unknown (21.9 % over the last 7 days, against 45.8 % proven cryptographically and the rest claimed by a pool API or inferred), and that number is reported as-is rather than smoothed over. The reasons are structural:

  • Qubic publishes no block list and no view key, nobody can attribute it.
  • Solo miners are invisible by design; that is the point of Monero.
  • Some pools expose no working API and publish no view key.
  • Very recent blocks may be genuinely unattributable for a few minutes, unless the pool provides a view key, in which case they are proven immediately.

Every public tracker hits this same ceiling; comparable sites report an even larger unknown share. Shrinking that fifth is the main open work on the project.

Merge mining

A merge-mined block is one where a single proof-of-work claims both a Monero block and a block on an auxiliary chain. The indexer detects it from the 0x03 tag in the coinbase tx_extra; the share over a window is published by /chain/provenance and marked with an M on each block in the fork visualizer.

It is tracked because it is a centralization vector: an auxiliary chain can subsidise miners and pull hashrate toward whichever pool supports it, and concentrated hashrate is what makes a reorganization possible in the first place.

It is not, however, what Qubic did. Qubic ran a "useful proof-of-work" scheme: it paid miners in its own token by converting the mined XMR, at one point roughly three times more lucrative than ordinary mining, taking its share from under 2 % in May 2025 to a claimed majority by August. The subsidy was economic, routed through a pool, not merge mining through the coinbase. Conflating the two would be wrong.

About 60 % of blocks are merge-mined over the last 7 days. That share is not a network-wide drift, it is a per-pool policy: supportxmr, p2pool, c3pool and moneroocean merge-mine every block they find, hashvault most of them, while nanopool, herominers, xmrpool and kryptex merge-mine none. The curve therefore tracks which pool is finding blocks this week as much as any change in practice. The auxiliary chain is not named: the on-chain tag carries only a Merkle root, so identifying it would require querying that chain. We report the count and the root rather than guessing.

Data model

PostgreSQL, read-only from the API's point of view. Two tables carry the chain record (k8s/monerometrics/20-configmap-postgres-init.yaml):

  • blocks, whose primary key is the block hash, not the height. This is deliberate: it lets several blocks coexist at the same height (the canonical one plus the orphans left by a reorg). A partial unique index (UNIQUE (height) WHERE is_canonical) guarantees exactly one canonical block per height at any instant. pool_source records how the pool was established for that block, so a consumer can weigh a cryptographic proof differently from a pool's own claim. reward_xmr is an exact NUMERIC, never a float.
  • reorgs_detected, one row per detected event: fork_point_height, depth, old_chain_tip_hash, new_chain_tip_hash, affected_tx_count, detected_at. fork_point_height is the first height that was replaced: the event rewrote heights fork_point_height to fork_point_height + depth - 1.

The orphan/canonical split is what powers the fork visualizer and the /orphans/recent and /reorgs/stats endpoints.

The rest are time series, created by the worker on first run:

TableWritten byHolds
mempool_snapshotsworker, each pollPending transaction count over time
fee_snapshotsworker, every 5 minThe node's four fee tiers, in piconero per byte
price_snapshotsworker, every 10 minCentralized spot plus the Haveno book, both sides
haveno_offersworker, every 10 minIndividual open Haveno offers with their payment method
haveno_tradesworker, hourlyExecuted Haveno trades back to May 2024, with payment method
haveno_liquidityworker, hourlyHourly resting liquidity per market, back to November 2024
spot_dailyworker, hourlyDaily centralized close, used to price historical trades
pool_sourcesworkerReachability and block count per pool API
api_usageAPIExternal request counter

Every one of them is a cache, not a source of truth: the chain tables are re-derivable from any monerod, and the market tables from haveno.markets. That property is what makes the high-availability plan below cheap.

Two series are the exception and cannot be rebuilt: mempool snapshots and reorgs. Once transactions are mined, nothing on-chain says how many were pending an hour ago, and a discarded block exists in no explorer. Both start the day this observatory began watching, and are kept rather than pruned for that reason.

API reference

The API (apps/api/) is read-only and returns JSON, built with FastAPI, with an interactive OpenAPI schema at api.monerometrics.net/docs. Heavy aggregations are cached server-side, and every response carries its own lifetime (Cache-Control + ETag), so a client that already holds a version gets an empty 304. Every IP is rate-limited (300 requests/minute; Tor traffic has its own shared bucket). CORS is open for GET. No key, no account, no tracking.

window accepts 1h, 24h, 7d, 30d, 90d, 1y, 5y unless noted otherwise.

Shortened here. Read the whole README on GitHub.

Tools it offers (19)

What this server listed when Ahel dialed its public endpoint in Oct 2026, with no key and no account of yours. The names are the server’s own.

  • network_info
  • price
  • haveno_book
  • haveno_premium
  • haveno_payment_methods
  • network_hashrate
  • network_blocktime
  • network_mempool
  • network_emission
  • reorgs
  • reorg_stats
  • recent_orphans
  • pool_distribution
  • pool_sources
  • chain_provenance
  • get_block
  • search_block
  • lookup_transaction
  • chain_fork_window

Signals

GitHub stars
3
Last commit
Oct 2026
Advanced
Delivery
monerometrics MCP server → your Ahel connector (mcp.ahel.ai) → your AI.
Item type
mcp-server
Key
io-github-nowi333-monerometrics
Source
github.com/nowi333/monerometrics
Hosted endpoint
https://api.monerometrics.net/mcp