mnr
Get a free token

mnr — Protocol & Development Architecture

an RPC network for Monero

Status Design document for the network direction. Does not replace Monero RPC Gateway - Development Plan v1.md; that document is the gateway direction and is kept as an alternative. Where the two share material (method policy, cache safety, timeouts), this document references it rather than repeating it.
Date 2026-09-03
Name mnr (lowercase wordmark; the consonants of Monero). Spoken as "Monero network" or spelled out "M-N-R" — both are fine and both are expected. Written on every page as "mnr — an RPC network for Monero", never "the Monero network". Protocol at mnr.network; hostname scheme in §4.9. Package/crate prefix mnr, binaries mnr-agent, mnr-relay, mnr-client.
Companion mnr - Business Model and 18-Month Plan.md
Reading order §1 what it is → §2 roles and trust → §3 protocol → §4 component design → §5 build plan → §6 difficulties register → §7 open questions

1. What is being built

A permissionless network in which independent operators run monerod behind a small agent, relayers aggregate many operators into one verified, cached, metered endpoint, and clients (wallets, swap backends, autonomous agents) pay in XMR — either to a relayer for one URL, or, later, directly to operators. The protocol, operator agent, relayer, and client library are open source (AGPL-3.0 for binaries, CC-BY for the spec). The founders participate as the first operators and the reference relayer; they do not own the network.

Design principles, in priority order:

  1. Verify, don't trust. Wherever Monero data is self-authenticating (block hashes, tx hashes, header chains), the relayer or client checks it. Trust is used only where verification is impossible (mempool, node info), and there it is replaced by agreement across independent operators.
  2. No token, no stake, no slashing. Operators are paid in XMR for verified work; bad work is recorded in a public, cryptographically verifiable fault log that anyone can weigh. Nothing is locked, nothing is confiscated.
  3. Stock wallets must work. A wallet must be able to use the network with nothing more than --daemon-address. Direct multi-operator verification is a client-library feature, not a requirement.
  4. Hobbyist operators must be able to join in ten minutes with a one-line install, no view key, no public IP if they use Tor.
  5. The reference relayer must be replaceable. Everything a relayer knows that others would need (operator directory, fault log, settlement statements) is published in a form another relayer can consume.
  6. Both sides of the market are paid from day one. Operators through the pool with a probation lane for newcomers; distributors (wallets, agents, interfaces) through an affiliate share. These are the two THORChain mechanisms that demonstrably bootstrapped an anonymous-operator network, and they are protocol features here, not marketing programmes.

Out of scope, permanently: monero-wallet-rpc methods, mixnets / decoy traffic, any on-chain protocol changes to Monero. Out of scope for the first 18 months: I2P ingress (planned, not built), payment channels of any kind, cross-relayer traffic sharing.


2. Roles, identities and trust boundaries

2.1 Roles

Role Runs Holds Sees Paid by
Operator monerod + mnr-agent Ed25519 operator key; Monero payout subaddress; no view key needed Request bodies from relayers (not client IPs, unless the relayer is misconfigured) Relayers (settlement pool), later clients directly
Relayer mnr-relay (+ mnr-store storefront, monero-wallet-rpc view-only) Ed25519 relayer key; client tokens (hashed); its own header chain; cache; metering DB Client IPs (unless client uses the relayer's .onion), request bodies, everything Clients (subscriptions / XMR402 credit)
Client Any Monero software, optionally mnr-client A bearer token (relayer mode) or an XMR402 credit (direct mode)
Affiliate Nothing (a wallet, agent framework, swap interface or integrator that puts its id on tokens it brings) Ed25519 affiliate key; payout address Relayers (15% of referred client revenue, from the relayer's share)
Directory signers mnr-directory signer tooling Ed25519 signer keys (threshold t-of-n, initially 1-of-1 = founders, target 3-of-5 by month 15) Operator/relayer registration records Nobody (public good)

2.2 Identity and keys

2.3 Trust boundaries (who can hurt whom)

Threat Mitigation
Operator serves wrong block/tx/header Relayer verifies hash; issues signed fault record; operator is deprioritised network-wide.
Operator serves wrong get_info/fee/mempool Majority agreement across ≥3 operators for consensus state; mempool is annotated as per-operator and never treated as authoritative.
Operator under-reports or relayer under-pays Both meter; settlement statements are signed and published; discrepancy > 2% is visible to everyone. There is no proof-of-serving; the remedy is reputation and exit.
Relayer logs or de-anonymises clients Cannot be prevented by protocol; mitigated by open source, .onion ingress, direct mode, and multiple relayers. Stated plainly in docs.
Sybil operators (one person, many identities) Pool payout is pro-rata by verified served work, so Sybils earn nothing extra per unit of capacity; diversity scoring (ASN, /24, latency fingerprint) limits how many "independent" votes one host can have in agreement checks.
Relayer and operator are the same entity (self-dealing) Allowed and expected (we are both). Settlement statements show it; clients can prefer relayers with more external supply.
Directory signers are compromised or coerced Records are individually operator-signed, so a malicious snapshot can omit operators but not forge them; multiple mirrors; threshold signing from month 15; relayers cache the last good snapshot and continue on stale data with a warning.
Hard-fork desync across independent operators Agent enforces a minimum monerod version published in the directory snapshot; operators below it are marked stale 2 weeks before a fork height and excluded from consensus checks after it.

3. Protocol specification (v0 — what the spec document will contain)

The spec is a separate, versioned document (mnr-spec). This section fixes the decisions so engineering can start; wire formats are finalised in the spec.

3.1 Directory

Operator record (CBOR, signed by operator key):

{ v: 1, id: <op pubkey>, endpoints: [{kind: "https"|"onion"|"i2p", url, spki?}],
  node: {pruned: bool, restricted: true, version: "0.18.x", chain: "mainnet"|"stagenet"},
  capacity: {rps_light: u32, mbps_stream: u32},         // self-declared, verified by relayers
  payout: [{addr: <monero subaddress or integrated addr>, bps: u16}, …],   // splits must sum to 10000; see "pooled operators" below
  min_payout_xmr: f64,
  accepts_direct: bool,                                  // phase 3
  votes: {param_name: value, …},                         // optional; §3.7
  contact: <optional onion/simplex/matrix string>, ts: unix, sig }

Pooled operators (borrowed from THORChain's pooled nodes): payout is a list of splits, so a person with capital can fund a box that a skilled operator runs, with the split (e.g. operator 2,000 bps, funder 8,000 bps) declared in the signed record. Relayers pay each address its share in the same settlement transaction. No protocol state beyond the record; the two parties' agreement is their own business.

Directory snapshot (CBOR, threshold-signed): {v, seq, ts, min_monerod_version, fork_height?, operators: [record…], relayers: [record…], fault_log_urls: [], sigs: []}. Distributed over: HTTPS mirrors (≥3, different hosts), .onion, and a DNS TXT seed carrying the current mirror list and snapshot hash (like Monero's seed nodes). Snapshot size at 500 operators ≈ 250 KB; refreshed hourly; sequence numbers prevent rollback.

Registration: the agent POSTs its signed record to any directory mirror; the record is queued; a relayer (initially ours) probes the endpoint for correctness (height, restricted RPC, version, TLS pin) and the signer includes it in the next snapshot. There is no fee and no bond. Removal is by the operator (signed tombstone) or by the signers (record omitted; reason published in the fault log).

3.2 Relayer ↔ operator session

  1. Relayer opens TLS (pinned) or Tor connection to the agent's endpoint and requests /mnr/v1/session with {relayer_id, nonce_r}.
  2. Agent replies {nonce_a}; relayer returns sig_relayer(nonce_a || nonce_r || op_id); agent verifies against the relayer's published key (agent fetches the directory too) or, for unknown relayers, its own allow_unknown_relayers setting (default true, so new relayers can bootstrap; operators may restrict).
  3. Agent issues a session id (random 128-bit) valid 24 h; all subsequent RPC requests carry Mnr-Session: <id>. Requests without a valid session hit the agent's public policy: by default deny (private operator), optionally free-tier (small unauthenticated allowance — how public nodes join without changing their habits).
  4. Both sides count per-session: light_requests, stream_bytes, errors, first_ts, last_ts. Counters are persisted every 10 s (SQLite) so restarts do not lose more than 10 s.

Metering unit: work units (WU) = light_requests × 1 + stream_MB × 20 (one MB of get_blocks.bin costs an operator roughly what 20 light calls do; the coefficient is a directory-snapshot parameter so it can be tuned network-wide without a release).

3.3 Settlement

3.4 Verification rules (the heart of the network)

The relayer maintains its own header chain: for every height, (hash, prev_hash, timestamp, height) — 32 + 32 + 8 + 8 bytes ≈ 80 B × ~3.5 M blocks ≈ 280 MB on disk, built once from operators by majority and then extended at the tip by agreement. RandomX proof-of-work is not verified (too expensive, and irrelevant: an operator that fabricates a PoW-valid alternate chain is a 51% attacker, which is not our threat model); linkage and majority are.

Data How verified On failure
get_block / get_block_header_by_hash Recompute block hash from the returned blob (Keccak over hashing blob: header ‖ tx-tree-hash ‖ tx count). Must equal the requested hash, or the header-chain hash at the requested height. Fault record; retry on next operator.
get_block_header_by_height, get_block_headers_range Header hash must equal the relayer's header chain at that height; if the relayer's chain is shorter, extend by majority first. Fault record.
/get_transactions For each tx: Keccak(tx blob) = txid (pruned txs: verify the pruned-hash form). block_height/in_pool fields must be consistent with header chain (height ≤ tip). Fault record.
/get_blocks.bin (stream) Relayer parses the epee stream incrementally, hashes every block, and checks linkage (prev_id) and equality with the header chain. CPU cost is real (§6.2); the relayer may sample-verify (every block header linkage, full hash on 1-in-k blocks, k configurable, default 4) when overloaded and record the verification level in metrics. Abort stream, fault record, restart from last verified height on another operator (clients handle short reads).
/get_outs.bin, get_output_distribution, get_output_histogram Immutable but not self-authenticating from a single response. Two-operator agreement on first request; cached with the tip−10 rule afterwards. Both answers cached as "disputed", third operator breaks tie; loser gets fault record.
get_info, get_height, get_last_block_header, get_fee_estimate, hard_fork_info Majority of ≥3 operators on height and top_block_hash (SWR-cached, §3.6); fee estimate = median. Node-specific fields normalised (see gateway plan §3.3). Degraded mode: serve highest-height operator, suspend immutable cache writes, alert.
Mempool methods Not verifiable. Annotated Mnr-Operator: <id>; never cached.
send_raw_transaction Broadcast to all healthy operators; success if ≥1 OK. Result header Mnr-Relayed: k/n. If all reject with the same reason (e.g. double spend), return it verbatim.

Fault record (CBOR, signed by relayer): {relayer_id, op_id, ts, method, request_hash, response_hash, expected, got, evidence_url?, sig}. The evidence (request + response blobs) is stored by the relayer for 30 days so anyone can reproduce the check. Fault logs are append-only files served over HTTPS/onion; relayers and mnr-client fetch the logs of relayers they choose to believe (initially the reference relayer's; a relayer can mark others' logs as trusted in config). An operator's fault rate = faults / verified requests over 30 days, decayed; ranking multiplies capacity weight by (1 − fault_rate)^8, so 5% faults roughly halves an operator's traffic and 20% removes them from the pool for practical purposes.

3.5 Client access

Relayer mode (v1): exactly the token scheme from the gateway plan §3.1 — path token or Basic auth, 256-bit, hashed at rest, rotation with grace — but issued by the relayer's storefront. Purchases via invoice page or XMR402: the storefront responds 402 with {amount_xmr, address, memo, expires}; the client pays; on 10 confirmations (or a relayer-chosen lower threshold for small credits) the storefront issues the token. Credits are denominated in WU (same unit as settlement) so that a client's balance and an operator's payout are commensurable. Purchase requests may carry affiliate_id (query parameter on the invoice page, header in the XMR402 flow); it is bound to the token for its lifetime and shown on the client's statement.

Direct mode (phase 3): mnr-client fetches the directory, picks k operators by diversity + fault rate + latency, and performs the verification rules itself. Payment: XMR402 credit purchased from an operator whose accepts_direct is true (the agent gets an optional view-only wallet for this; hobbyists stay relayer-only). Stock wallets never use direct mode; it is for backends, agents and wallet developers who embed the library.

3.6 Caching and policy

Identical to the gateway plan §3.3 (tip−10 immutable rule, SWR 1/5/15 for consensus state, per-tx cache, never cache mempool, method allow-list, per-method timeouts) with one change: cache keys include the verification level (full | sampled | agreed) so that a sampled-verified stream never satisfies a request that asked for full verification.

3.7 Network parameters and work-weighted votes (THORChain's "mimir", without a token)

The directory snapshot carries a small set of network parameters: wu_stream_per_mb (20), op_share (0.60, band 0.50–0.70), probation_weight (0.10), affiliate_default (0.15), min_monerod_version, fork_height, tip_safety_depth (10). Until month 15 the directory signers set them. From then on, an operator may include a votes map in its signed record; when the signers build a snapshot they compute, per parameter, the median of votes weighted by each operator's verified WU over the trailing 90 days (from the published settlement statements of all relayers the signers recognise) and adopt it if at least 40% of network-wide verified WU has voted. Voting weight is work actually served and verified, not capital and not identity — the only Sybil-resistant quantity this network has. Relayers are not obliged to follow the snapshot's op_share, but the reference relayer does, and their published value is visible to operators choosing where to serve.


4. Component design

All core components are Rust (tokio, hyper/axum, rustls). Rationale: epee parsing and hashing on streamed data is CPU-bound and must be memory-safe; one language across agent, relayer and client library; existing crates (monero-serai/monero-rs families) for serialization and hashing; arti for Tor as a later option. TypeScript is used only for the storefront UI and the optional wallet-plugin glue.

4.1 mnr-core (library crate)

Modules: wire (epee binary + JSON-RPC types for the daemon API, hand-verified against monerod 0.18.x fixtures), hash (Keccak-256, block hashing blob, tx-tree hash, pruned tx hash), verify (rules of §3.4 as pure functions over bytes), policy (method table; the single source of truth rendered into docs), directory (record/snapshot types, signing, validation), metering (WU accounting), identity (Ed25519 keys, bech32m ids), settlement (statement types, pool math). No I/O in this crate; it must be fuzzable (cargo fuzz targets for every parser).

4.2 mnr-agent (operator binary)

[ clearnet TLS :443 ] ─┐
[ tor hidden svc    ] ─┼─▶ ingress (axum) ─▶ session auth ─▶ local policy (restricted-only) ─▶ proxy ─▶ monerod 127.0.0.1:18081
[ i2p (later)       ] ─┘        │                 │                          │
                               │                 └── unknown-session policy (deny | free-tier bucket)
                               ├── metering (per-session counters → SQLite, 10 s flush)
                               ├── health (self-probe monerod every 5 s: height, sync, peers, disk)
                               ├── directory client (fetch snapshot hourly; enforce min version; refresh own record daily)
                               └── statements viewer (GET /mnr/v1/statements; compares relayer statements to own counters)

Properties: single static binary (musl), ~10 MB; config is one TOML file; mnr-agent init generates keys, detects monerod, writes config, registers with the directory, and prints the operator id; mnr-agent doctor checks restricted RPC, version, disk, port reachability, Tor status. It does not manage monerod (operators keep their setup) but ships a recommended systemd unit and flags. Memory budget < 100 MB; it must run on the same box as a pruned node on a 4 GB VPS. Streaming responses are proxied without buffering. No view key, no wallet, no payments in v1 — the agent is deliberately boring.

4.3 mnr-relay (relayer binary)

client ─▶ ingress (axum; clearnet + onion) ─▶ auth (token hash → SQLite/Postgres; isolate cache) ─▶ rate limit (governor; per-token bucket + WU balance)
      ─▶ policy (mnr-core::policy) ─▶ cache (in-memory LRU + on-disk immutable store, key includes verification level)
      ─▶ dispatcher (operator pool: rank by on_tip, ema_latency, fault_rate, diversity; retry-once; broadcast for writes)
      ─▶ verifier (mnr-core::verify; streaming for .bin; sample-verify under load) ─▶ response (+ Mnr-* headers)

background tasks: header-chain sync (majority extend at tip; reorg detection → epoch bump of cache)
                  operator prober (every 10 s: get_info + last header; EMA; on_tip)
                  directory sync (hourly) + fault-log publisher (append-only, signed)
                  settlement (weekly: pool math, statements, payout via wallet-rpc hot wallet, dev fee line)
                  metrics (Prometheus /metrics, no per-client labels except hashed prefix in error samples)

State: SQLite for a single-node relayer (our v1); Postgres option for larger relayers. Cache: moka in-memory (bounded, e.g. 2 GB) + rocksdb on-disk store for immutable blocks/txs (bounded by size, LRU eviction). The relayer runs on 2 boxes in active/active behind DNS with health-checked failover in v1 (no Cloudflare required; optional Cloudflare in front for those who want it — off by default in the reference deployment). Each box also runs Tor for the .onion ingress; both boxes publish the same onion via OnionBalance.

4.4 mnr-store (storefront + billing, part of the relayer repo)

Axum routes + a small server-rendered UI: tier/credit purchase, invoice page with live confirmations, XMR402 endpoints, token issuance/rotation, statements for clients (WU balance). Watches a view-only monero-wallet-rpc for incoming payments; sweeps to cold on a schedule. A separate spend-capable monero-wallet-rpc with a capped float (refilled manually from cold) performs operator payouts; it is the one hot key in the system and is treated as such (own box, firewalled, cap = one epoch's pool + 10%).

4.5 mnr-client (library) and integrations

Rust library with C ABI, plus thin TypeScript and Python packages generated from it. Provides: directory fetch + selection, relayer-mode helper (token handling, rotation), direct-mode verified calls (phase 3), XMR402 purchase flow. A local proxy mode (mnr-client proxy --listen 127.0.0.1:18089) lets stock wallets use direct mode without embedding the library — the wallet points at localhost and the proxy does selection and verification. This is how Feather/Cake/CLI users get direct mode without those projects changing anything.

4.6 mnr-directory

Signer CLI (mnr-dir sign, mnr-dir add, mnr-dir tombstone) producing snapshots; a static-file publisher for mirrors; a registration intake (small axum service) that queues records for probing. Threshold signing uses FROST (Ed25519) via an existing Rust crate when the signer set expands (month 15); until then a single key with an offline backup.

4.7 mnr-sim (test network harness)

Docker-compose bringing up N stagenet monerod + agents, one relayer, a fault injector (serve wrong block, lag height, drop streams, return alt-chain), synthetic clients (wallet-sync replay, get_info storm, broadcast). Used in CI nightly and for every release. This is not optional; a network protocol without a simulator will ship bugs that only appear with strangers' nodes.

4.8 Repository layout

mnr/
├── spec/                 protocol spec (markdown, versioned; wire formats, verification rules, settlement)
├── crates/core/          mnr-core
├── crates/agent/         mnr-agent
├── crates/relay/         mnr-relay (+ store/ module)
├── crates/client/        mnr-client (+ ffi/, bindings/ts, bindings/py)
├── crates/directory/     mnr-directory
├── sim/                  mnr-sim docker harness, fault injector, k6/replay clients
├── deploy/               ansible for our operators + relayer boxes; systemd units; Tor/OnionBalance config
├── docs/                 operator guide, relayer guide, client guide, wallet how-tos, generated method table
└── .github/              CI: fmt, clippy, tests, fuzz smoke, sim nightly, release builds (musl, arm64)

4.9 Hostnames

Hostname Role Notes
mnr.network Protocol site: spec, docs, relayer list, transparency reports Neutral; lists all relayers, ours first only while it is the only one
rpc.mnr.network Reference relayer client endpoint (/v1/<token>/json_rpc) Run by the founders' entity; disclosed as such on the site. Health-checked DNS across the two relayer boxes; .onion published beside it via OnionBalance
stagenet.rpc.mnr.network Stagenet endpoint Handed to THORChain-side teams in week 1
node.mnr.network Operator onboarding: agent download, mnr-agent init instructions, earnings dashboard "Run a node, get paid"
dir1. / dir2. / dir3.mnr.network Directory mirrors Different hosts; each with a .onion; deliberately separate from rpc. so the neutral part is visibly not the commercial part
pay.mnr.network Storefront, invoices, XMR402 endpoints, token rotation Reference relayer's; other relayers run their own
status.mnr.network Status page (phase 2) Static host, outside the relayer boxes

5. Build plan

Team assumption: two Rust engineers (one with Monero internals experience) plus one operator/ops person half-time. The schedule below already carries slack for review cycles, hard-fork interruptions and the two-sided bootstrap; with a single engineer, P3 slips past month 18 and P4 becomes year 2. Weeks are from 2026-09-14; months match the business doc (month 1 = October 2026).

Phase Weeks (months) Scope Exit criteria
P0 — Spec & core 1–8 (m1–2) mnr-spec v0 (directory, session, metering, settlement, verification, fault records); mnr-core wire + hash + verify with fixtures from mainnet/stagenet; fuzz targets; policy table ported from the gateway plan. Every §3.4 rule has a passing test against real monerod responses; fuzzers run 1 h with no crashes; spec reviewed by ≥2 outside Monero developers.
P1-min — THORChain interface track 4–10 (m1–3) A deliberately minimal relayer for one customer class: THORChain interfaces and bots that will need a Monero daemon when XMR swaps are enabled (chain client shipped in v3.20 on 2026-08-25; enablement gated on a validator churn with inspection, then shadow liquidity — expected within weeks). Scope: token auth, policy table, SWR/immutable cache, dispatcher over our 3 operators, JSON-method verification (block/tx hash, header chain), send_raw_transaction fan-out, get_blocks.bin passthrough without stream verification (annotated Mnr-Verify: none), invoice storefront, clearnet + onion. No agent sessions yet (our operators are reached over WireGuard), no settlement. KYC.RIP-style integration test passes; two THORChain interface or bot teams on Business tokens by week 10; p95 get_fee_estimate/get_outs under 150 ms from EU/NA. This track is a subset of P1, not a fork: everything built here is P1 code shipped early.
P1 — Seed network 6–26 (m2–6) mnr-agent v0.1 (session auth, metering, health, directory client, Tor ingress); mnr-relay v0.1 (auth, rate limit, policy, cache, dispatcher, verifier for JSON methods, prober, header chain); mnr-directory single-signer; deploy 3 own operators + 2 relayer boxes; mnr-sim with 5 nodes; storefront with invoices (no XMR402 yet). Stock CLI wallet syncs from scratch through the relayer; kill an operator → no client-visible failure; injected wrong block → fault record produced and operator deprioritised; dogfood tenants (KYC.RIP, Ripley) live.
P2 — Money & strangers 24–44 (m6–10) Stream verification for get_blocks.bin (sampled + full); settlement engine (pool math, statements, payouts, dev fee line, affiliate payouts, payout splits, probation lane); agent statements viewer; XMR402 purchase flow; operator onboarding docs + mnr-agent init/doctor; fault-log publishing; diversity scoring; invited operator beta (10 external operators); min_monerod_version enforcement. Four consecutive weekly settlements paid on mainnet with < 2% discrepancy for all external operators; sim run with 30 nodes and 3 hostile passes; external operator can join in ≤ 10 min following docs only.
P3 — Direct mode & federation 44–65 (m11–15) mnr-client (Rust + TS/Py) with direct-mode verification and local-proxy mode; agent optional accepts_direct with view-only wallet; second relayer operated by a partner using our binary; relayer records in directory; threshold-signed snapshots (3-of-5); work-weighted parameter votes (§3.7); reorg/fork drill at a real hard-fork; I2P ingress prototype. A Feather user syncs via mnr-client proxy against 3 operators with no relayer; partner relayer settles its own operators; directory snapshot signed by 3 independent parties.
P4 — Hardening 65–78+ (m16–18, continuing) Performance (relayer to 5k rps light / 200 concurrent streams per box), Postgres backend, operator-side free-tier policy, arti evaluation, spec v1.0 freeze, security audit of mnr-core parsers. Spec v1.0 published; audit findings closed; ≥3 relayers, ≥40 operators live (see business doc for targets).

Definition of done per release: cargo test, fuzz smoke, mnr-sim nightly green, changelog, reproducible builds (musl, x86-64 + arm64), signed release artifacts, and docs regenerated from policy.


6. Development difficulties register

Ordered by how likely they are to cost the schedule. Each has a mitigation and an owner-type.

6.1 Monero serialization and hashing correctness (very likely, high cost)

The epee binary format and the block hashing blob have subtle rules (varints, nested sections, tx-tree hash for 1/2/n transactions, pruned tx hash composition). Existing Rust crates cover most of it but lag hard forks and have had divergence bugs. Mitigation: treat mnr-core::wire/hash as the highest-risk code; build a fixture corpus from real chain data (every hard-fork boundary block, blocks with 0/1/2/many txs, coinbase-only, pruned and unpruned tx forms); differential-test against monerod on every CI run; fuzz continuously; budget half of P0 here. Owner: the Monero-internals engineer.

6.2 Stream verification CPU cost (certain, medium cost)

Hashing every block in a get_blocks.bin stream at wallet-sync rates (tens of MB/s per client) costs real CPU; 200 concurrent syncs could saturate a relayer box. Mitigation: sampled verification (default 1-in-4 full hash, all linkage checks) with the level recorded in cache keys and headers; per-box concurrency cap on streams; measure at P2 and size relayer boxes from data. Direct mode moves this cost to the client, which is the correct long-term place for it.

6.3 Metering without proof (certain, medium cost, reputational)

There is no cryptographic proof that a request was served. Operators must trust relayer statements, checked against their own counters. Mitigation: dual metering with published statements, 2% tolerance, signed dispute records, and the social fact that relayers compete for operators. Do not attempt per-request signed receipts (they double latency and solve a problem the market solves). Document this honestly in the operator guide.

6.4 Two-sided bootstrap (certain, business-critical)

Operators join for income; income needs clients; clients need enough operators for diversity. Mitigation: the seed phase is us on both sides (3 operators, 1 relayer, 2 dogfood clients); external operators are invited first (10 known community node runners) with a guaranteed minimum pool for the first 3 months funded from relayer take; only then open registration. See business doc §6.

6.5 Directory centralisation (certain, political)

A single-signer directory is a central point, and the community will say so. Mitigation: records are operator-signed (the signer cannot forge), mirrors are independent, relayers survive on stale snapshots, and threshold signing with named independent signers ships by month 15. Say all of this on day one.

6.6 Hard forks with strangers' nodes (certain around each fork)

Independent operators upgrade late. Mitigation: min_monerod_version + fork_height in the snapshot; agents warn 2 weeks ahead; relayers exclude stale operators from consensus checks at the fork height; a fork drill in mnr-sim before each real fork.

6.7 Tor performance for streams (likely)

get_blocks.bin over Tor is slow (single-circuit throughput often < 1 MB/s). Mitigation: relayers prefer clearnet-pinned operators for streams and Tor operators for light calls; onion operators are still valuable (mempool, broadcast, consensus votes). Clients using the relayer's .onion ingress get the Tor speed they chose.

6.8 XMR402 immaturity (likely)

XMR402 is a young protocol; its credit/receipt semantics may change. Mitigation: the storefront implements XMR402 behind a small adapter; our WU-credit token format is ours and stable; coordinate with Ripley's maintainers early (they are a dogfood tenant).

6.9 Hot-wallet risk in settlement (certain, security)

Weekly payouts need a spend-capable wallet online. Mitigation: capped float (one epoch + 10%), separate box, manual refill from cold, payout tx built and signed only by the settlement task, alerting on any outbound above the cap. Consider a 2-of-3 multisig with an offline co-signer when the pool exceeds ~20 XMR/epoch.

6.10 Operator privacy exposure (certain, communicative)

Operators see wallet-sync patterns of relayer clients. This is the standard remote-node exposure, and the network makes it broader (more parties) but shallower (no IP). Mitigation: relayer strips IPs; docs state it; direct-mode clients spread queries; the honest recommendation for high-privacy users is to run their own node — and mnr-agent makes that node earn money, which is the network's best privacy argument.

6.11 Abuse of free-tier and open registration (likely)

Junk operators (stale, pruned-when-claiming-full, flapping) and freeloading clients. Mitigation: probing before inclusion, fault log, capacity weighting only after 7 days of verified service, and a free tier that is a relayer policy (small WU allowance per token, one token per invoice page visit) rather than a network guarantee.

6.12 Legal shape of paying operators (unknown, jurisdiction-dependent)

Weekly XMR payouts to pseudonymous operators may look like a marketplace or a payment service in some jurisdictions. Mitigation: counsel review before P2 payouts go live; structure the relayer as the contracting party with operators under published terms; keep the protocol itself entity-less. Not a technical problem, but it can block P2.

6.13 THORChain traffic shape and timing (likely, schedule pressure)

Interfaces and bots on THORChain's XMR pools need get_fee_estimate, get_outs.bin (ring construction), /get_transactions, send_raw_transaction, and inbound-detection scans — bursty, latency-sensitive, and heavier on get_outs than wallet traffic. They will need it the week swaps are fully enabled — likely before the P1 relayer exists — and they will pick a provider during the shadow-liquidity period, not after. Mitigation: the P1-min track (§5) ships the subset they need in ~10 weeks over our own operators only; get_outs gets two-operator agreement from day one because it is exactly the data a swap interface cannot afford to get wrong. Do not stretch P1-min into P1: it exists to catch a window, and its unverified-stream mode is labelled as such in headers and docs. Risk on the other side: THORChain has paused chain integrations before, and the inspection-and-shadow sequence can slip; treat this demand as an accelerant, never as the base case for operator payouts.

6.14 Affiliate accounting (certain, low)

Affiliate payouts are a second weekly payment stream with its own statements and disputes. Mitigation: the same settlement engine and statement format as operators (an affiliate is a directory identity with a payout address); the share is paid only on revenue actually received, so there is nothing to claw back; self-referral is explicitly allowed so there is nothing to police.

6.15 Solo-developer risk (structural)

The plan assumes two engineers. If it is one person, P3 slips beyond month 18 and the spec review must lean on outside contributors. Mitigation: publish the spec and mnr-core early (end of P0) to attract contributors; the dev-fee line exists precisely so that maintenance has a funding floor that does not depend on grants.


7. Open questions for the founders

  1. Name and domain (mnr is a placeholder).
  2. License confirmation: AGPL-3.0 for agent/relay, MIT/Apache for mnr-core and mnr-client (so wallets can embed them) — recommended split.
  3. Whether the reference relayer's .onion is the primary advertised endpoint or the clearnet one (affects marketing more than engineering).
  4. Whether to seek spec review from specific Monero developers before P1, and whether to submit mnr-core hashing tests upstream to the crates it depends on.
  5. Which 10 operators to invite for the P2 beta (community node runners with a track record).
  6. Counsel jurisdiction for §6.12 — the same open item as the gateway direction's ops doc.
  7. Which THORChain interfaces and bot operators to approach in week 1 for the P1-min track (their requirements shape the get_outs agreement rule and the burst limits).
  8. Affiliate default (15%) and probation weight (10%) — both are snapshot parameters, but the initial values set expectations.

This document reuses the method policy, cache-safety rules, timeouts and token scheme from Monero RPC Gateway - Development Plan v1.md §3; those sections remain the reference for that material.