{
  "markdown": "# monerometrics\n\n> A reorg-aware observatory for the health of the Monero network: public dashboard + API.\n\n[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n[![IaC](https://img.shields.io/badge/IaC-Terraform%20%2B%20Ansible-7B42BC?logo=terraform)](https://www.terraform.io/)\n[![Monero](https://img.shields.io/badge/Monero-XMR-FF6600?logo=monero)](https://www.getmonero.org/)\n\n`monerometrics` measures and historizes the health of the Monero network: network hashrate,\nblock time, mempool state, mining-pool distribution and, above all, **chain reorganizations\n(reorgs) and orphan blocks**, which most block explorers surface poorly.\n\nThe project was born from the **August 2025 Qubic episode**, during which a mining pool paying\nminers in its own token approached a majority of the network hashrate, then withheld blocks and\nreleased a longer private chain. On 14 September 2025 that produced an 18-block reorganization at\nheight 3,499,659 that erased about 36 minutes of history and invalidated 118 transactions, well\npast the 10 confirmations everyone treated as final. The public debate lacked reliable, accessible\ndata to settle it. monerometrics fills that gap with a neutral, verifiable, reorg-aware observatory.\n\nIt is **open-source and self-funded**, with no ads and no tracking. The dashboard and the API\nare **free and open, permanently**: there is no paid tier, and there will not be one.\n\n> **From a diploma project to a community tool.** monerometrics V1 was built and defended as a\n> French professional IT-infrastructure project, and it earned the diploma. With that chapter\n> closed, the goal of V2 is to hand the project over to the **Monero community**: fully\n> open-source, self-funded, and useful well beyond a classroom.\n\n- **Dashboard**: [monerometrics.net](https://monerometrics.net)\n- **Public API**: [api.monerometrics.net](https://api.monerometrics.net) (OpenAPI documented)\n- **MCP server**: `https://api.monerometrics.net/mcp` (Streamable HTTP; in the [MCP Registry](https://registry.modelcontextprotocol.io/?search=monerometrics))\n- **Tor hidden service**: `6wbhchvavey26lbtscl6w6qg76balycixtsklcggrsslyk4xah6sbbad.onion`\n  (dashboard + API, no Cloudflare, no IP exposure, see [Access over Tor](#access-over-tor))\n\n---\n\n## What it does\n\n- **Dashboard**: a React SPA organised in four sections. *Consensus and reorganizations*: an\n  interactive chain-fork visualizer that draws competing branches as a tree, plus reorg statistics\n  and orphan history. Each block names its pool and how the attribution was established\n  (proven, claimed or inferred), and merge-mined blocks are flagged. *Mining concentration*: pool\n  distribution with **largest-pool share and the Nakamoto coefficient**, and the evidence behind\n  each attribution. *Network state*: hashrate, block-time variance, mempool, emission, and a\n  **transaction fee estimator** reading the node's four priority tiers live. *Peer-to-peer market*:\n  Haveno premium over spot on **both sides of the book**, live order-book depth, resting liquidity,\n  and **premium by payment method**. Available in\n  **English, French and Spanish**, light/dark themes.\n- **Public API**: FastAPI, read-only JSON endpoints grouped by theme (service, network,\n  chain/reorgs, pools, market). Automatically documented via OpenAPI.\n- **Reorg detection**: a Python worker reads each block from a synced node, computes the\n  indicators and **detects reorganizations** by re-checking a rolling window of recent blocks\n  against the node, recording their real depth and the transactions they displaced.\n\n## Architecture\n\nA visitor reaches the dashboard or API through Cloudflare, which terminates TLS and applies its\nedge protections, then forwards to a hardened **edge** server (nginx + ModSecurity WAF). The\nedge serves the static dashboard and reverse-proxies the API to the **k3s** node, where the\napplication core runs: a Monero node, the indexer, PostgreSQL and the API.\n\nA second, independent path exists: a **Tor hidden service** running on the edge, which bypasses\nCloudflare entirely and serves both the dashboard and the API on a single origin.\n\n```mermaid\nflowchart LR\n    User([\"Visitor\"]) -->|HTTPS| CF[\"Cloudflare<br/>DNS · WAF · proxy\"]\n    CF -->|\"HTTP/2 origin pull\"| Edge\n    Tor([\"Tor visitor\"]) -.->|\"Tor network\"| Onion\n    subgraph HZ[\"Hetzner Cloud · private network 10.0.0.0/24\"]\n        Onion[\"tor daemon<br/>hidden service v3\"]\n        Edge[\"edge 10.0.0.20<br/>nginx + ModSecurity WAF<br/>static dashboard\"]\n        Onion -.->|\"127.0.0.1:8080\"| Edge\n        subgraph K3S[\"k3s 10.0.0.30 · no public ingress\"]\n            API[\"FastAPI\"]\n            Worker[\"Python worker<br/>(indexer)\"]\n            DB[(\"PostgreSQL 17\")]\n            Node[\"monerod 0.18<br/>pruned\"]\n        end\n        Edge -->|\"api. proxy_pass\"| API\n        API --> DB\n        Worker --> DB\n        Worker -->|JSON-RPC| Node\n    end\n    Node <-->|\"P2P sync\"| Monero([\"Monero network\"])\n    API -.->|\"XMR price (cached)\"| Price([\"CoinGecko · Kraken<br/>Haveno (RetoSwap)\"])\n```\n\n## Access over Tor\n\nThe dashboard is also published as a **Tor v3 hidden service**, giving a path to the data that\ndoes not depend on Cloudflare and does not expose the visitor's IP address:\n\n```\n6wbhchvavey26lbtscl6w6qg76balycixtsklcggrsslyk4xah6sbbad.onion\n```\n\nDesign notes:\n\n- **One origin, no leaks.** The hidden service serves the dashboard on `/` and reverse-proxies\n  the API under `/api`. The SPA detects an `.onion` host at runtime and switches to that\n  relative path, so **no browser request ever leaves the hidden service** for the clearnet.\n  A single build serves both clearnet and Tor.\n- **No TLS, on purpose.** Tor already encrypts and authenticates end to end, and the `.onion`\n  address *is* the service's public key. A certificate would add nothing.\n- **Not reachable from the internet.** The `.onion` vhost listens on `127.0.0.1:8080` only, so\n  the Tor daemon is the sole thing that can reach it, no firewall rule, no exposed port. Tor\n  makes outbound connections only; nothing inbound is opened.\n- **No logs.** Every request arrives from `127.0.0.1` (Tor carries no client IP), so access\n  logging is disabled on this vhost: it would record nothing useful.\n- **Discoverable.** The clearnet site advertises the service with an `Onion-Location` header,\n  so Tor Browser offers to switch automatically.\n- **Rate limiting.** Since per-IP limiting is meaningless over Tor, hidden-service traffic is\n  tagged by nginx and isolated in a dedicated bucket with a much higher ceiling, the API stays\n  protected from abuse without Tor users evicting each other. The tagging header is stripped on\n  the clearnet vhosts, so it cannot be forged from the internet.\n\nIt is deployed by the `tor` Ansible role (`config/ansible/roles/tor/`).\n\n## Infrastructure\n\nThe platform runs on **Hetzner Cloud** (region Nuremberg) as three Ubuntu 24.04 servers on a\nprivate network, each protected by its own Hetzner firewall. Administration is done over a\n**Tailscale** (WireGuard) zero-trust mesh; Grafana is reachable over Tailscale only, never from\nthe internet. The encrypted off-site backups live on a separate cloud (**Oracle Cloud**,\nS3-compatible object storage) to isolate failure domains.\n\n```mermaid\nflowchart TB\n    Admin([\"Administrator\"]) -->|\"SSH 22\"| Bastion\n    Internet([\"Internet\"]) -->|\"80 / 443\"| Edge\n    subgraph HZ[\"Hetzner Cloud (nbg1) · 10.0.0.0/24\"]\n        Bastion[\"bastion 10.0.0.10<br/>firewall: SSH from admin IP only\"]\n        Edge[\"edge 10.0.0.20<br/>firewall: 80/443 from internet\"]\n        K3s[\"k3s 10.0.0.30 + data volume<br/>firewall: no inbound\"]\n        Bastion -. ProxyJump .-> Edge\n        Bastion -. ProxyJump .-> K3s\n    end\n    Mesh[\"Tailscale mesh<br/>(admin · Grafana)\"]\n    Bastion --- Mesh\n    Edge --- Mesh\n    K3s --- Mesh\n    OCI[(\"Oracle Cloud<br/>Restic backups<br/>S3-compatible, encrypted\")]\n    K3s -->|\"3-2-1 encrypted\"| OCI\n```\n\n| Server | Type | Public exposure | Role |\n|---|---|---|---|\n| `bastion` | CX23 | SSH from admin IP only | Sole SSH entry point, ProxyJump to the others |\n| `edge` | CX23 | 80/443 from the internet | nginx reverse proxy + ModSecurity WAF, serves the static dashboard |\n| `k3s` | CX33 + 128 GB volume | none (outbound only) | k3s cluster: monerod, worker, PostgreSQL, API, OpenBao |\n\n### Running cost\n\nTaken from the Hetzner invoice, per hour of use, excluding VAT:\n\n| Item | Unit price | Monthly (730 h) |\n| --- | --- | --- |\n| 2 × CX23 (`bastion`, `edge`) | 0.0088 €/h | 12.85 € |\n| 1 × CX33 (`k3s`) | 0.0136 €/h | 9.93 € |\n| 3 × primary IPv4 | 0.0008 €/h | 1.75 € |\n| 128 GB volume | 0.0572 €/GB-month | 7.32 € |\n| **Total** | | **31.85 € excl. VAT · 38.22 € incl. VAT** |\n\nCloudflare, Let's Encrypt, Tailscale and GitHub Actions are on free tiers.\n\nThe volume is the item that moves. The pruned Monero blockchain occupies 104 GB of it, while the\nentire indexed database of 3.7 M blocks takes 2 GB, about 572 bytes per block.\n\nGrowth is measured from the indexed data rather than estimated. Summing the recorded size of every\ncanonical block gives 186.8 GB for the full chain against 104 GB on disk, so this pruned node keeps\n**56 %** of Monero. The chain grew **22.18 GB over the last twelve full months**, which is\n**12.35 GB a year on disk**, steady across years (17.5 GB in 2022, 18.3 in 2023, 29.1 in 2024,\n22.2 in 2025). At 93 % full, the 128 GB volume saturates in roughly eight months, so the plan is to take it\nto 160 GB (+1.83 €/month) and no further, since the same measurement says larger would sit unused.\n\nStorage is driven by the chain, not by indexing: the database adds about 150 MB a year.\n\nKey choices:\n\n- **Defense in depth.** Per-server firewalls, a single SSH entry point, a WAF on the only public\n  web surface, a zero-trust admin mesh, and a k3s node with **no inbound exposure at all**\n  (admin and edge reach it over the private network; its public IP is outbound-only, for node\n  sync and image pulls).\n- **HTTP/2 origin behind Cloudflare.** Both the client-facing (Cloudflare) and origin (nginx)\n  hops run HTTP/2, with the ModSecurity WAF fully active on HTTP/2 traffic.\n- **Everything is Infrastructure-as-Code.** Hetzner resources via **Terraform**\n  (`hcloud` + `cloudflare` providers), server configuration and CIS-aligned hardening via\n  **Ansible**. Container images are built and published to GHCR.\n- **Secrets.** The cluster is provisioned with **OpenBao** (a free fork of Vault) and the\n  manifests carry no plaintext credential. In the current state OpenBao is sealed, and the\n  workloads read their database credentials from a Kubernetes Secret\n  (`postgres-credentials-fallback`) instead. Unsealing is a manual step after any restart, which\n  is the trade-off of running a single node; the fallback keeps the service up meanwhile.\n- **Supervision** with **Prometheus + Grafana**; backups with **Restic** (3-2-1, cross-cloud,\n  tested restore, see [`k8s/monerometrics/BACKUP-PRA.md`](k8s/monerometrics/BACKUP-PRA.md)).\n\n## How the indexer works\n\nThe worker ([`apps/worker/indexer.py`](apps/worker/indexer.py)) is the heart of the project. Every\n`POLL_INTERVAL` seconds it asks `monerod` for its state (`/get_info`) and, when the node is\nsynced, runs **two passes** against the database:\n\n1. **Confirmation-window rescan (reorg detection).** It re-fetches the headers of the last\n   `CONFIRMATION_WINDOW` blocks (default 60) in a single `get_block_headers_range` call and\n   compares each block hash to the canonical hash already stored. Any mismatch is a\n   reorganization: the previously stored block is flagged **orphan** (`is_canonical = false`),\n   the node's new block becomes canonical, and a row is written to `reorgs_detected` with the\n   **real depth** (number of contiguous rewritten heights) and **affected transaction count**\n   (sum of the orphaned blocks' tx counts). This pass is what makes reorg detection actually\n   work, plain forward-only indexing never revisits the past, so it would silently miss every\n   reorg that rewrites already-indexed heights.\n\n   **The window is also the detection ceiling.** A reorganization deeper than\n   `CONFIRMATION_WINDOW` rewrites heights the rescan never looks at, so it would be missed\n   entirely rather than reported with a wrong depth. At 60 blocks the margin is wide: the\n   deepest Monero reorg on record, 14 September 2025, was 18 blocks. But the limit is\n   structural, not a tuning detail, and the counts published here are only complete up to\n   that depth. Raising it costs one larger header call per poll.\n\n   The transaction count is `num_txes` from the block header, which excludes the coinbase, so\n   it counts transactions users actually broadcast rather than the miner's own output.\n\n2. **Forward indexing.** It then fetches the new blocks above the last indexed height. Close to\n   the tip it pulls **full blocks** one by one for accurate pool attribution; when it is far\n   behind (fresh deploy), it switches to a **fast header backfill** (`get_block_headers_range`,\n   ~1000 blocks per call), which is enough for the network-health series and lets the long\n   windows (90 d, 1 y, 5 y) fill with real history in well under two hours instead of never.\n\n```mermaid\nflowchart TB\n    Start([\"Every POLL_INTERVAL\"]) --> Info[\"GET /get_info\"]\n    Info --> Sync{\"node synced?\"}\n    Sync -->|no| Wait[\"log progress · sleep\"]\n    Sync -->|yes| Rescan[\"Rescan last N blocks<br/>get_block_headers_range\"]\n    Rescan --> Diff{\"stored hash<br/>≠ node hash?\"}\n    Diff -->|yes| Reorg[\"mark old → orphan<br/>insert new canonical<br/>record reorg (depth, tx)\"]\n    Diff -->|no| Fwd\n    Reorg --> Fwd[\"Forward index<br/>new blocks (batch)\"]\n    Fwd --> Metrics[\"update Prometheus metrics\"]\n    Metrics --> Start\n```\n\n## Mining-pool attribution\n\nMonero is private by design: a coinbase transaction carries no pool name, and stealth addresses\nmean you cannot simply look up \"who was paid\". Attribution therefore has to be **established**,\nnever assumed, and every public tracker faces the same wall.\n\nmonerometrics never infers a pool's share from its **self-reported hashrate** (the number pools\npublish on their own site, which nothing on-chain backs). It only counts blocks it can tie to a\npool by evidence, and records **which kind of evidence** was used, per block, in `blocks.pool_source`.\n\n### The three methods, in order of strength\n\n**1. View-key proof, cryptographic (`viewkey_proof`)**\n\nSome pools publish their wallet **primary address** and **secret view key** (see\n[blocks.p2pool.observer/proofs](https://blocks.p2pool.observer/proofs)). A view key only reveals\n*incoming* transactions; it cannot spend, and the spend key is never disclosed. That is enough to\nprove ownership of a coinbase output, with no trust in anyone's API:\n\n```\nR          = transaction public key, parsed out of the coinbase tx_extra (tag 0x01)\nderivation = 8 · a · R                      (a = the pool's secret view key)\nP_expected = Hs(derivation ‖ varint(i)) · G + B    (B = the pool's public spend key)\n```\n\nIf `P_expected` equals the actual output key at index `i`, that output pays the pool's wallet:\na mathematical fact, independent of any pool API, and therefore **immune to the reporting lag**\nthat makes fresh blocks look unattributed. Implemented from scratch in\n[`apps/worker/pool_proofs.py`](apps/worker/pool_proofs.py) (ed25519 point arithmetic, Keccak-256,\nMonero base58), ~33 ms per block.\n\n**2. Pool block lists, cross-referenced (`pool_api`)**\n\nPools that publish no view key still publish the list of blocks they found. Those lists are\naggregated every ~2 minutes into an index `{block_hash → pool}` and matched **by block hash**, so\nthe claim is at least anchored to a real block on the canonical chain. The weakness is *latency*,\nnot correctness: a pool slow to publish leaves its own recent blocks looking `unknown` until it\ncatches up. A re-attribution pass on every cycle fixes those retroactively.\n\n**3. Coinbase heuristic, structural (`coinbase_heuristic`)**\n\nA coinbase paying many outputs at once is characteristic of P2Pool, which splits the reward\nbetween miners directly on-chain. Used only as a last resort, and labelled as a heuristic.\n\n### Guardrails\n\n- **Keys are self-checked at startup.** Each view key must prove a block that the pool's *own* API\n  claims. A key that fails is **dropped**, not used, so a stale or wrong key can never mislabel\n  blocks. Logged as `View-key self-check: N verified [...]`.\n- **Conflicts are surfaced, not hidden.** If a pool API claims a block that the proof attributes to\n  someone else, it is logged and counted in `monerometrics_attribution_conflicts_total`.\n- **Unproven claims are flagged.** A pool that publishes a view key should be able to prove its own\n  blocks. When it lists one its published key does *not* prove, the block is recorded as\n  `pool_api_unproven` rather than presented with the same confidence as a proven one, the key may\n  have rotated without being republished, or the claim may simply be wrong. Counted in\n  `monerometrics_unproven_claims_total` and surfaced on the dashboard.\n- **Proof outranks APIs.** When both are available, the cryptographic result wins.\n- **Source health is public.** Reachability and block count per source are published at\n  [`/pools/sources`](https://api.monerometrics.net/pools/sources), so a silently failing source is\n  visible instead of quietly inflating `unknown`.\n\n### Sources aggregated\n\n| Pool | Endpoint | Method / depth | View-key proof |\n|---|---|---|---|\n| supportxmr.com | `www.supportxmr.com/api/pool/blocks` | `?limit=` (up to 10000) | ✅ |\n| hashvault.pro | `api.hashvault.pro/v3/monero/pool/blocks` | `?limit=&page=0` (up to 10000) | ✅ |\n| moneroocean.stream | `api.moneroocean.stream/pool/blocks` | `?limit=100` (pool cap) | ✅ |\n| xmrpool.eu | `web.xmrpool.eu:8119/get_blocks` | paginated by `?height=` | ✅ |\n| ownblock.xyz | ((no block API) |) | ✅ |\n| p2pool (main) | `p2pool.observer/api/pool/blocks` | `?limit=` (up to 1000) | · |\n| p2pool (mini) | `mini.p2pool.observer/api/pool/blocks` | `?limit=` | · |\n| p2pool (nano) | `nano.p2pool.observer/api/pool/blocks` | `?limit=` | · |\n| nanopool.org | `xmr.nanopool.org/api/v1/pool/blocks/0/{n}` | path count (~4600) | · |\n| c3pool.com | `api.c3pool.org/pool/blocks` | `?limit=` (up to 10000) | · |\n| kryptex.com | `pool.kryptex.com/xmr/api/v1/pool/blocks` | paginated via `next` (~100, pool cap) | · |\n| herominers.com | `monero.herominers.com/api/get_blocks` | paginated by `?height=` | · |\n| monerohash.com | `monerohash.com/api/get_blocks` | paginated by `?height=` | · |\n\nP2Pool runs three sidechains (main/mini/nano); all three are polled and collapsed into a single\n`p2pool` label, since they are one decentralised network from a centralisation standpoint.\n\n### What stays unattributable, and why\n\nAbout a fifth of blocks end up `unknown` (20 % over the last 7 days, against 40 % proven\ncryptographically and 38 % claimed by a pool API), and that number is reported as-is rather than\nsmoothed over. The reasons are structural:\n\n- **Qubic publishes no block list at all** and no view key, nobody can attribute it.\n- **Solo miners** are invisible by design; that is the point of Monero.\n- **Some pools expose no working API** (DxPool returns HTTP 500 on every documented endpoint) and\n  publish no view key.\n- **Very recent blocks** may be genuinely unattributable for a few minutes, until the pool that\n  found them publishes, unless the pool provides a view key, in which case they are proven\n  immediately.\n\nEvery public tracker hits this same ceiling; comparable sites report an even larger unknown share.\nThe honest move is to show it.\n\n### Merge mining\n\nA merge-mined block is one where a single proof-of-work claims both a Monero block and a block on\nan auxiliary chain. The indexer detects it from the `0x03` tag in the coinbase `tx_extra` and stores\nthe count in `blocks.merge_mining`; the share over a window is published by\n[`/chain/provenance`](https://api.monerometrics.net/chain/provenance) and marked with an `M` on each\nblock in the dashboard's fork visualizer.\n\nIt is tracked because it is not a curiosity but a centralization vector: an auxiliary chain can\nsubsidise miners and pull hashrate toward whichever pool supports it, and concentrated hashrate is\nwhat makes a reorganization possible in the first place.\n\n**It is not, however, what Qubic did.** Qubic ran a \"useful proof-of-work\" scheme: it paid miners\nin its own token by converting the mined XMR, at one point roughly three times more lucrative than\nordinary Monero mining, taking its share of the network from under 2 % in May 2025 to a claimed\nmajority by August. The subsidy was economic, routed through a pool, not merge mining through the\ncoinbase. The mechanisms are distinct even though both end in concentrated hashrate, and conflating\nthem would be wrong.\n\nAround 61 % of blocks are merge-mined over the last 30 days, down from 74 % in August 2025. That\nshare is not a network-wide drift: it is a per-pool policy. supportxmr, p2pool, c3pool and\nmoneroocean merge-mine every block they find, hashvault 95 % of them, while nanopool, herominers,\nxmrpool and kryptex merge-mine none. The curve therefore tracks **which pool is finding blocks this\nweek** as much as any change in practice.\n\nThe auxiliary chain is **not named**: the on-chain tag carries only a Merkle root, so identifying it\nwould require querying that chain. We report the count and the root rather than guessing.\n\n**Observability.** The worker exposes Prometheus metrics on `:9100/metrics`, indexing lag, reorg\ncounter, sync state, pool-index size, blocks proven by view key, attribution conflicts, last-loop\ntimestamp, and writes a heartbeat file consumed by a Kubernetes liveness probe, so a stalled loop\ngets restarted automatically.\n\n## Data model\n\nPostgreSQL, read-only from the API's point of view. Two tables carry the chain record and matter\nmost ([`k8s/monerometrics/20-configmap-postgres-init.yaml`](k8s/monerometrics/20-configmap-postgres-init.yaml)):\n\n- **`blocks`**, the primary key is the **block hash**, *not* the height. This is deliberate: it\n  lets several blocks coexist at the same height (the canonical one plus the orphans left behind\n  by a reorg). A **partial unique index** (`UNIQUE (height) WHERE is_canonical`) guarantees there\n  is exactly one canonical block per height at any instant. Columns include `height`, `prev_hash`,\n  timestamps, `difficulty`, `tx_count`, `miner_pool`, `reward_xmr` (stored as an exact `NUMERIC`,\n  not a float) and the `is_canonical` flag. **`pool_source`** records *how* the pool was\n  established for that block (`viewkey_proof`, `pool_api` or `coinbase_heuristic`) so a\n  consumer can weigh a cryptographic proof differently from a pool's own claim, and `NULL` for\n  blocks indexed before provenance tracking existed.\n- **`reorgs_detected`**, one row per detected reorganization event: `fork_point_height`, `depth`,\n  `old_chain_tip_hash`, `new_chain_tip_hash`, `affected_tx_count` and `detected_at`.\n\nThe orphan/canonical split is what powers the dashboard's chain-fork visualizer and the\n`/orphans/recent` and `/reorgs/stats` endpoints.\n\nThe rest are time series and caches, created by the worker on first run rather than by the init\nmanifest:\n\n| Table | Written by | Holds |\n|---|---|---|\n| `mempool_snapshots` | worker, each poll | Pending transaction count over time |\n| `fee_snapshots` | worker, every 5 min | The node's four fee tiers, in piconero per byte |\n| `price_snapshots` | worker, every 10 min | Centralized spot plus the Haveno book, both sides: best and average offer, resting liquidity and offer count for asks and bids, and the raw book |\n| `haveno_offers` | worker, every 10 min | Individual open Haveno offers with their payment method |\n| `haveno_trades` | worker, hourly | Executed Haveno trades back to May 2024, with payment method |\n| `haveno_liquidity` | worker, hourly | Hourly resting liquidity per market, back to November 2024 |\n| `spot_daily` | worker, hourly | Daily centralized close, used to price historical trades |\n| `pool_sources` | worker | Reachability and block count per pool API |\n| `api_usage` | API | External request counter |\n\nEvery one of them is a **cache, not a source of truth**: the chain tables are re-derivable from any\n`monerod`, and the market tables from `haveno.markets`. That property is what makes the high-availability\nplan below cheap.\n\n## API reference\n\nThe API ([`apps/api/`](apps/api/)) is **read-only** and returns JSON. It is built with FastAPI,\nso an interactive OpenAPI schema is served at\n[`api.monerometrics.net/docs`](https://api.monerometrics.net/docs) (raw schema at `/openapi.json`).\nResponses for the heavy aggregations are cached (~60 s) and every IP is rate-limited\n(300 requests/minute by default; Tor hidden-service traffic gets its own shared bucket with a\nhigher ceiling, since per-IP limiting is meaningless there); CORS is open for `GET` so the API\ncan be consumed from anywhere. No key, no account, no tracking.\n\n`window` accepts `1h`, `24h`, `7d`, `30d`, `90d`, `1y`, `5y` unless noted otherwise.\n\n**Service**\n\n| Endpoint | Description |\n|---|---|\n| `GET /health` | Liveness + database connectivity check. |\n| `GET /info` | Global metadata: API version, latest indexed height, total blocks, orphans, reorgs. |\n| `GET /usage/external` | Count of external API requests served, excluding this dashboard and the MCP server. |\n\n**Network**\n\n| Endpoint | Description |\n|---|---|\n| `GET /network/info` | Current state: sync status, mempool size, difficulty, estimated hashrate (live from the node). |\n| `GET /network/hashrate?window=` | Historical network hashrate (difficulty / 120 s), bucketed by the window. |\n| `GET /network/blocktime?window=` | Variance of the time between consecutive canonical blocks (target 120 s). `window` = `1h\\|24h\\|7d\\|30d`. |\n| `GET /network/mempool?window=` | Mempool size (pending transactions) over time, sampled each worker poll. |\n| `GET /network/emission?window=` | Average block reward over time · Monero tail emission (~0.6 XMR/block). `window` excludes `1h`. |\n| `GET /network/fees` | The node's four fee tiers (slow, normal, fast, fastest), priced for a reference ~1500-byte transaction in XMR and USD. |\n| `GET /network/fees/history?window=` | Normal-tier fee over time, in nanonero, for the reference transaction size. `window` accepts `24h`, `7d`, `30d`, `90d`, `1y`. |\n\n**Chain & reorgs**\n\n| Endpoint | Description |\n|---|---|\n| `GET /chain/window?from=&to=` | Raw block window between two heights (max 1000 blocks). |\n| `GET /chain/provenance?window=` | Evidence quality of our own attribution over the window: how many blocks were proven cryptographically, claimed by a pool API, inferred structurally, or left unattributed · plus claims a pool could not prove with its own published key. `window` = `1h\\|6h\\|24h\\|48h\\|7d`. |\n| `GET /chain/block/{hash}` | Full detail for one block, read live from the node: coinbase hash, weight and long-term weight, included transaction hashes, merge-mining tags, plus the pool attribution and (for proven blocks) the public proof inputs (wallet address and view key) so anyone can re-verify it. |\n| `GET /chain/fork-window?limit=` | Latest N blocks including orphans, with fork-point flags (powers the chain visualizer). `limit` = 10..500. |\n| `GET /reorgs?limit=` | Most recent detected reorganizations. `limit` = 1..1000. |\n| `GET /reorgs/stats` | Reorg statistics aggregated over 24h / 7d / 30d (count, avg/max depth, affected tx). |\n| `GET /orphans/recent?limit=` | Recent orphan blocks with their competing canonical block. `limit` = 1..500. |\n\n**Mining pools**\n\n| Endpoint | Description |\n|---|---|\n| `GET /pools/distribution?window=` | Block share per pool over the window, plus decentralization metrics: largest-pool share and **Nakamoto coefficient**. Unattributed blocks stay in the denominator but are credited to no pool, so the coefficient is a ceiling: if they belong to pools already listed, the true value can only be lower. `window` = `1h\\|6h\\|24h\\|48h\\|7d`. |\n| `GET /pools/sources` | Reachability of each pool API used for attribution (status measured by the indexer, not by your browser). |\n\n**Market**\n\n| Endpoint | Description |\n|---|---|\n| `GET /price` | XMR/USD from a centralized reference (CoinGecko, with Kraken as fallback) **and** the Haveno peer-to-peer street price (RetoSwap network, via `haveno.markets`). Returns `ask_premium_pct` and `bid_premium_pct`, the best offer on each side over spot, their amount-weighted counterparts, `round_trip_cost_pct`, and the legacy `premium_pct` computed from the last traded price. Both sources are proxied and cached server-side (~5 s) so the browser never calls them directly. |\n| `GET /price/spread?window=` | Haveno order book against centralized spot over time, sampled every 10 minutes, **both sides**: lowest ask and highest bid, amount-weighted average of each side, resting liquidity and offer count per side, plus `round_trip_cost_pct`. `window` accepts `24h`, `7d`, `30d`, `90d`, `1y`. |\n| `GET /haveno/methods?window=&currency=` | Executed Haveno trades grouped by **payment method**, with average, median and standard deviation of the premium over centralized spot. `window` accepts `30d`, `90d`, `180d`, `1y`, `all`; `currency` accepts `USD`, `EUR`. |\n| `GET /haveno/liquidity?window=&currency=` | XMR resting in open Haveno offers, hourly, back to November 2024. `currency` accepts `USD`, `EUR`, `AUD`, `GBP`. |\n| `GET /haveno/trades?limit=&currency=` | Recent executed Haveno trades with payment method, price and premium. |\n| `GET /news` | Releases and announcements from the Monero project's own blog, fetched server-side and cached 30 min. Only the `releases` and `announcements` categories; aggregators and newsletters are deliberately excluded, and links outside `getmonero.org` are dropped so a compromised feed cannot redirect visitors. Entries appear roughly every two weeks, so each carries its date. |\n| `GET /status` | One-line health verdict — chain and mining concentration — computed from the largest-pool share, the Nakamoto coefficient, 24h reorg depth and tip age. Returns **every threshold that produced it**: nothing in the protocol defines a pool share as high, so ours are published rather than implied. |\n| `GET /haveno/book` | The **live order book** for `XMR_USD`, both sides, as price levels with cumulative depth, offer count, payment methods and a `reversible` flag, each priced against spot. Also returns the amount-weighted average of each side and `round_trip_cost_pct`. Cached ~30 s. |\n\n**Discovery.** Beyond the documented API, the service answers the agent-discovery conventions crawlers\nactually ask for: `llms.txt`, `agents.json`, agent cards, `mcp.json`, OpenRPC, `ai-plugin.json`, x402,\n`owners.json`, and the OAuth protected-resource metadata at both the bare path and the RFC 9728 form\nwith the resource path appended (`/.well-known/oauth-protected-resource/mcp`). Serving these cut the\n404 rate on agent traffic from 95% to a few dozen a day.\n\nRequests for endpoints that do not exist get a JSON body listing the interfaces that do, rather than a\nbare 404. What the service deliberately does **not** answer is `/v1/models` and its variants: those\nprobes look for an OpenAI-compatible inference API, and answering them would advertise a capability\nthis project does not have.\n\n### The price of a payment rail\n\nThe interesting question about a no-KYC exchange is not what Monero costs there, it is **what makes\nit cost more**. Grouping every executed trade by payment method answers it, and the answer is not\nthe intuitive one:\n\n| Payment method | Trades | Volume (XMR) | Avg premium | Reversible |\n|---|---:|---:|---:|---|\n| PayPal | 63 | 25 197 | +14.67% | yes |\n| Wise (TransferWise USD) | 86 | 15 756 | +13.60% | yes |\n| Cash App | 249 | 67 824 | +10.77% | yes |\n| Venmo | 30 | 9 569 | +8.92% | yes |\n| Zelle | 466 | 298 361 | +2.60% | no |\n| US postal money order | 8 | 4 615 | +1.46% | no |\n| Revolut | 89 | 42 209 | +1.41% | yes |\n| Cash by mail | 219 | 455 897 | +1.37% | no |\n\n*USD market, 180 days to 24 August 2026, spot reference Kraken daily close.*\n\n**The premium tracks reversibility, not privacy.** A buyer who pays by PayPal or Cash App can file a\nchargeback after the Monero has already been released, and there is no recourse, so sellers price\nthat risk in, at nine to fifteen percent. Rails that cannot be reversed sit near one to three\npercent. Cash in an envelope, the most private method on the list, is among the *cheapest*, and\ncarries the largest volume of any rail.\n\nThe `reversible` flag is **our classification, not a Haveno field**. Payment methods with only a\nhandful of trades produce a fragile premium. The spot reference is a daily close, so intraday moves\nadd noise, and trades before September 2024 fall outside Kraken's 720-day window and carry no\npremium at all. Crypto pairs are excluded: `haveno.markets` quotes them inverted, and a premium\nagainst a fiat spot would be meaningless.\n\n**Reading the Haveno premium.** Both sides of the book are exposed, and the numbers do not mean the\nsame thing. On the sell side, `ask_premium_pct` compares the **lowest ask** to centralized spot and\n`ask_avg_premium_pct` compares the **amount-weighted average of every sell offer**. On the buy side,\n`bid_premium_pct` and `bid_avg_premium_pct` do the same for the offers that buy your XMR. The gap\nbetween best and average is the shape of one side: at the time of writing the top of book sits at\n+0.2% while the average sell offer sits at +8.3%, so the cheapest offer tracks spot and the depth\ndoes not.\n\nThe gap between the two **averages** is `round_trip_cost_pct`, the cost of buying and selling back,\nand it is the number that survives contact with reality: 21.9% at the time of writing, against an\n8.3% headline premium. It is that large because the two sides are not symmetrical — 33.7 XMR of sell\ndepth against 44.6 XMR of buy depth, with the buy side averaging −11.2%. Quoting only the sell-side\npremium understates the real cost of using this market by more than a factor of two.\n\nIt is deliberately built from the **amount-weighted averages rather than the best offer on each\nside**, so read it as an upper bound. Pairing the two best offers would produce a flattering number\nthat no serious size can actually obtain: the best offer in this book has been backed by as little as\n1.49 XMR, around 650 USD. A small trade that never leaves the first price level does better than\n`round_trip_cost_pct`; a trade that walks the book does not. `/haveno/book` is where you see at which\nlevel the price starts to degrade.\n\n`premium_pct` compares the **last traded price** to spot; that fill may be hours old in a thin book,\nso it can overstate the premium by ten points or more, and it is kept only for backward compatibility.\n\n**Scope: fiat markets only.** Haveno runs 34 markets. The headline liquidity figure on\n`haveno.markets` aggregates all of them and sits around 7,900 XMR, but roughly **95% of that is crypto\npairs**: BTC/XMR alone holds about 2,000 XMR and stablecoins another 3,000. Every fiat market combined\nis only a few hundred. We index fiat on purpose. A crypto-to-crypto swap is instant, riskless and needs\nno counterparty, so those offers cannot drift far from exchange spot and their premium is always near\nzero. Fiat peer-to-peer involves a real person, chargeback exposure and delay, and that is the only\nreason a premium exists at all. Every figure here is the `XMR_USD` market unless stated otherwise, so\nit will read far smaller than the site-wide total, by design.\n\n**What these numbers are not.** The aggregated series behind `/price/spread` carry price, amount and\noffer count, but **not the payment method** behind each offer, because the level-1 depth feed does not\nexpose it. A Haveno offer settled by instant bank transfer and one settled by cash in the mail carry\nvery different privacy (and very different premiums) yet appear identically in those series. The\nlowest ask is therefore not, on its own, the price of buying Monero privately; it is the price of the\nmost competitive offer, whatever its payment rail. `/haveno/book` is the exception: it reads the\nlevel-2 feed, so each price level there does carry its payment methods and a `reversible` flag.\nNote also that `XMR_USD`\nis fiat US dollars: `haveno.markets` lists `USDT-ERC20`, `USDT-TRC20`, `USDC-ERC20` and `DAI-ERC20`\nas separate markets, so this pair is not a stablecoin quote.\n\nKnown limits, stated rather than discovered: only the USD pair carries meaningful volume on Haveno;\noffers are advertisements with differing payment methods rather than a matched order book, so the\nhighest bid can sit above the lowest ask; and history starts on 24 August 2026, when recording began,\nbecause `haveno.markets` exposes no historical series and the spread cannot be backfilled.\n\n## MCP server\n\nThe same read-only metrics are exposed to AI assistants through a **Model Context\nProtocol** server ([`apps/mcp/`](apps/mcp/)), so any MCP-compatible client (Claude,\nIDE agents, …) can query the Monero network directly, no account, no API key.\n\n- **Endpoint** (Streamable HTTP): `https://api.monerometrics.net/mcp`\n- **Registry**: published to the official [MCP Registry](https://registry.modelcontextprotocol.io/?search=monerometrics) as `io.github.nowi333/monerometrics`.\n- **Tools**: `network_info`, `network_hashrate`, `reorgs`, `reorg_stats`, `recent_orphans`,\n  `pool_distribution` (largest-pool share + Nakamoto coefficient), `chain_provenance`,\n  `search_block` (by height or hash, down to the genesis block), `get_block`,\n  `chain_fork_window`, `price`, and more. Plus a `monerometrics://reference` resource.\n\nIt is a thin wrapper over the public REST API (one small FastMCP service), deployed\nalongside the API on k3s and routed at `/mcp`.\n\n## Repository layout\n\n```\napps/          Application code\n  dashboard/   React + Vite SPA (EN/FR/ES)\n  api/         FastAPI service\n  mcp/         Model Context Protocol server (thin wrapper over the API)\n  worker/      Python indexer (reorg detection) + shared pool module\ninfra/         Terraform, modules (network, server, dns) + environments\nconfig/        Ansible, inventory, playbooks, roles (hardening, nginx, tor, k3s, ...)\nk8s/           Kubernetes (k3s) manifests + backup/DR runbook (BACKUP-PRA.md)\nscripts/       Helpers (env loader)\n```\n\n## Deploying\n\nThe whole platform is reproducible from code. With a Hetzner project, a Cloudflare-managed\ndomain and the required tokens in your environment:\n\n```bash\n# 1. Load tokens (HCLOUD_TOKEN, CLOUDFLARE_API_TOKEN, TAILSCALE_AUTH_KEY, GHCR) from the keychain\nsource scripts/load-env.sh\n\n# 2. Provision the servers, private network, firewalls and DNS records\ncd infra/environments/poc\nterraform init\nterraform apply        # creates bastion, edge, k3s + Cloudflare A records\n\n# 3. Configure and harden the servers (CIS L1, nginx+WAF, k3s, data volume, Tailscale)\ncd ../../../config/ansible\nansible-playbook site.yml\n\n# 4. Deploy the application workloads on k3s\nkubectl apply -k k8s/monerometrics/\n```\n\nServer sizing, datacenter and the data-volume size are Terraform variables\n(see [`infra/environments/poc/terraform.tfvars.example`](infra/environments/poc/terraform.tfvars.example)).\n\n**Secrets live in OpenBao**, and no plaintext credential is committed to the manifests. Seed the\ndatabase credentials once, and every consumer (PostgreSQL, worker, API, backup) reads them from\nthere. If OpenBao is sealed, the workloads fall back to a Kubernetes Secret so the service keeps\nrunning until it is unsealed:\n\n```bash\n# Database credentials (read by PostgreSQL, worker, API, backup)\nbao kv put secret/postgres/credentials \\\n  POSTGRES_USER=monerometrics POSTGRES_DB=monerometrics POSTGRES_PASSWORD='<strong-password>'\n\n# Backup credentials, Restic repository + OCI S3-compatible keys (read by the backup job)\nbao kv put secret/restic/credentials \\\n  RESTIC_REPOSITORY='s3:https://<oci-endpoint>/<bucket>' RESTIC_PASSWORD='<restic-password>' \\\n  AWS_ACCESS_KEY_ID='<key>' AWS_SECRET_ACCESS_KEY='<secret>' AWS_DEFAULT_REGION='<region>'\n```\n\nOpenBao Kubernetes auth roles must allow: `monerometrics-postgres` / `-worker` / `-api` to read\n`secret/postgres/credentials`, and `monerometrics-backup` to read **both**\n`secret/postgres/credentials` and `secret/restic/credentials`.\n\n## Local development (dashboard)\n\n```bash\ncd apps/dashboard\nnpm install\nnpm run dev      # local dev server\nnpm run build    # production build to dist/\n```\n\nThe dashboard reads the public API; point it at `api.monerometrics.net` (see `src/api.js`).\n\n## Toward high availability (target architecture)\n\n> **These topologies are the production target, not what runs today.** The live platform is a\n> deliberately lean **single-node POC**: one k3s node (a single point of failure), one\n> unreplicated PostgreSQL, one edge. It is honest, cheap (~38 €/month including VAT) and enough to prove the\n> product, but the k3s node, the database and the edge are all SPOFs. The plan below removes\n> them **in tiers**, each independently fundable, so infrastructure grows with the project's\n> community funding rather than ahead of it.\n\nOne property makes high availability unusually cheap here: **every metric is deterministically\nderived from the Monero blockchain**. The database is a materialized cache, not a source of\ntruth, any replica or whole region can be **re-indexed from its own local `monerod`**. Reads are\ntherefore naturally active-active, and losing a stack means rebuilding from first principles, not\nlosing data.\n\n### Tier 1, Highly-available application, single region\n\nRemove every in-region SPOF: a load balancer in front of a redundant edge pool, a 3-node k3s\ncontrol plane (etcd quorum), a replicated PostgreSQL with automatic failover, OpenBao in Raft HA,\nand redundant Monero nodes.\n\n```mermaid\nflowchart TB\n    CF[\"Cloudflare · DNS / WAF / proxy\"] --> LB[\"Hetzner Load Balancer\"]\n    subgraph R1[\"Hetzner region · one datacenter\"]\n        LB --> E1[\"edge-1<br/>nginx + WAF\"]\n        LB --> E2[\"edge-2<br/>nginx + WAF\"]\n        subgraph K3S[\"k3s HA cluster\"]\n            direction LR\n            S1([\"server-1<br/>etcd\"]) --- S2([\"server-2<br/>etcd\"]) --- S3([\"server-3<br/>etcd\"])\n            AG[\"agent nodes<br/>API · worker pods\"]\n        end\n        E1 --> AG\n        E2 --> AG\n        subgraph PG[\"PostgreSQL HA · CloudNativePG\"]\n            PGp[(\"primary\")]\n            PGr1[(\"replica\")]\n            PGr2[(\"replica\")]\n            PGp --> PGr1\n            PGp --> PGr2\n        end\n        AG -->|writes| PGp\n        AG -.reads.-> PGr1\n        subgraph BAO[\"OpenBao · Raft\"]\n            B1([\"bao-1\"]) --- B2([\"bao-2\"]) --- B3([\"bao-3\"])\n        end\n        AG --> BAO\n        subgraph NODES[\"Monero nodes\"]\n            N1[\"monerod-1\"]\n            N2[\"monerod-2\"]\n        end\n        AG --> N1\n        AG --> N2\n    end\n    N1 <--> MN([\"Monero P2P network\"])\n    N2 <--> MN\n    R1 -->|\"encrypted 3-2-1\"| OCI[(\"off-site backups<br/>Oracle Cloud\")]\n```\n\n### Tier 2, Multi-zone HA (one provider, several datacenters)\n\nSpread the cluster across Hetzner locations (e.g. `nbg1` / `fsn1` / `hel1`). The etcd quorum and\nedge pool survive the loss of an entire zone; a **synchronous** PostgreSQL replica in a second\nzone gives near-zero RPO on failover, with an asynchronous copy in a third.\n\n```mermaid\nflowchart TB\n    CF[\"Cloudflare · DNS / WAF / proxy\"] --> LB[\"Hetzner Load Balancer<br/>spreads across zones\"]\n    subgraph Z1[\"Zone A · nbg1\"]\n        E1[\"edge-1\"]\n        S1([\"k3s server-1 · etcd\"])\n        PGp[(\"Postgres primary\")]\n    end\n    subgraph Z2[\"Zone B · fsn1\"]\n        E2[\"edge-2\"]\n        S2([\"k3s server-2 · etcd\"])\n        PGs[(\"Postgres sync replica<br/>RPO ≈ 0\")]\n    end\n    subgraph Z3[\"Zone C · hel1\"]\n        E3[\"edge-3\"]\n        S3([\"k3s server-3 · etcd\"])\n        PGa[(\"Postgres async replica\")]\n    end\n    LB --> E1\n    LB --> E2\n    LB --> E3\n    S1 --- S2\n    S2 --- S3\n    PGp ==>|\"synchronous\"| PGs\n    PGp -->|\"asynchronous\"| PGa\n    Z1 -->|encrypted| BK[(\"cross-region backups\")]\n    Z2 -->|encrypted| BK\n    Z3 -->|encrypted| BK\n```\n\n### Tier 3, Multi-region active-active (aspirational)\n\nTwo (or more) full stacks in different regions/providers, steered by **Cloudflare Load\nBalancing** with health checks, geo-routing to the nearest healthy region and automatic failover.\nEach region indexes from its **own** Monero nodes, so read traffic is served locally and a region\ncan be rebuilt independently; only the write path needs coordinated replication.\n\n```mermaid\nflowchart TB\n    Users([\"Visitors worldwide\"]) --> CFLB[\"Cloudflare Load Balancing<br/>health checks · geo-steering · failover\"]\n    CFLB --> RA\n    CFLB --> RB\n    subgraph RA[\"Region A, Hetzner (DE)\"]\n        EA[\"edge pool + WAF\"]\n        KA[\"k3s HA<br/>API · worker\"]\n        NA[\"monerod nodes\"]\n        DA[(\"Postgres primary\")]\n        EA --> KA\n        KA --> DA\n        KA --> NA\n    end\n    subgraph RB[\"Region B, second provider / region\"]\n        EB[\"edge pool + WAF\"]\n        KB[\"k3s HA<br/>API · worker\"]\n        NB[\"monerod nodes\"]\n        DB[(\"Postgres replica<br/>(re-derivable)\")]\n        EB --> KB\n        KB --> DB\n        KB --> NB\n    end\n    DA <-.->|\"logical replication\"| DB\n    NA <--> MN([\"Monero P2P network\"])\n    NB <--> MN\n    RA -->|encrypted| BK[(\"global backups\")]\n    RB -->|encrypted| BK\n```\n\n## Security & secrets\n\n- **No secret in the repo.** Credentials are pulled from the macOS Keychain / environment at\n  runtime (`scripts/load-env.sh`) or stored in OpenBao. Terraform state is kept out of the repo.\n- Defense in depth: firewall segmentation, SSH bastion, WAF, zero-trust admin mesh, risk analysis\n  (EBIOS RM) and a tested cross-cloud disaster-recovery plan.\n\n## Contact\n\nQuestions about the methodology, a number that looks wrong, a pool wanting its view key indexed,\nor a researcher after a dataset: **contact@monerometrics.net**. Bug reports and feature requests\nare better as [GitHub issues](https://github.com/nowi333/monerometrics/issues), where they stay\npublic and searchable.\n\n## Support the project\n\nmonerometrics runs on a modest self-funded infrastructure (no ads, no tracking, no data sold),\nabout 38 € a month including VAT. Donations in XMR go to:\n\n```\n41mkUSrcAvdGw9E19a83rsh9zdSNC7m8PP34NvmRCCPLZVot61kJHc9i8KGge5JmxkDTuiz7a2nUtE7C4rcQJn4xKjfFyU2\n```\n\nAny other cryptocurrency works too, settled to that address with no account and no KYC, through\n[trocador.app AnonPay](https://trocador.app/anonpay/?ticker_to=xmr&network_to=Mainnet&donation=True&name=monerometrics&description=Support%20monerometrics&buttonbgcolor=ff6600&address=41mkUSrcAvdGw9E19a83rsh9zdSNC7m8PP34NvmRCCPLZVot61kJHc9i8KGge5JmxkDTuiz7a2nUtE7C4rcQJn4xKjfFyU2).\n\nThe same address is published on the dashboard and in\n[`/.well-known/owners.json`](https://api.monerometrics.net/.well-known/owners.json), so it can be\ncross-checked against three independent sources before you send anything.\n\n## License\n\nMIT, see [`LICENSE`](LICENSE).\n\n---\n\n**No advertising. No tracking. No data sold. Just Monero network data, done honestly.**\n",
  "bytes": 47167,
  "sha": "7d65f4ed1e20f2464da27b50d9a2c92208fab580d379379126e957c666c132fd",
  "repo_slug": "nowi333/monerometrics",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_nowi333_monerometrics_066e4580/readme"
}