MiroShark logo
Documentation · v1

How MiroShark works

A deep walkthrough of the engine, by category.

MiroShark is a universal swarm-intelligence engine. You bring a scenario; it builds the world around it - a temporal knowledge graph, hundreds of grounded personas, three platforms running in lockstep, and a citing report at the end. Here is exactly how each layer is wired.

Last updated · July 23, 2026

MiroShark ships fast - this page is a snapshot, not a contract. For the latest features, env vars, model defaults and breaking changes, check the repo directly: recent commits, open & merged PRs, README and docs/. When in doubt, the code on main is the source of truth.

Overview

The five-step pipeline

Document in, world out. Every simulation walks the same five stages: build a graph, ground the personas, run three platforms in parallel, write a citing report, and let you interact.

1 · Graph build

A document is parsed into a Neo4j knowledge graph, written in parallel via batched UNWIND transactions.

2 · Agent setup

Personas are generated from the graph, each grounded in five layers of context - attributes, relationships, semantic search, BFS neighbours and an optional live web call.

3 · Simulation

Twitter, Reddit and Polymarket run at once via asyncio.gather. A market-media bridge links traders and social agents; beliefs and trust update each round.

4 · Report

A ReACT agent writes the post-mortem from simulation_feed, market_state, graph search and belief trajectory. Every claim cites a real post or trade.

5 · Interaction

DM any agent, message a group, inject breaking news, or fork the timeline with a counterfactual event.

Output surfaces

Every run yields a citing report, trajectory chart, signal JSON and badge, exports and an optional on-chain anchor - 37 surfaces listed in /api/surfaces.json.
document  ─▶  graph build  ─▶  persona grounding  ─▶  3× platform loop  ─▶  report
                                                       │
                                                       ├─ Director Mode  (inject)
                                                       ├─ Fork           (branch)
                                                       └─ Persona Chat   (DM)
Memory

Knowledge graph & memory pipeline

A Neo4j-backed temporal graph - NER, entity resolution, contradiction detection, fused retrieval, and Leiden community clusters - inspired by Hindsight, Graphiti, Letta and HippoRAG.

Ingestion

Text flows through NER into a batched embedding call (OpenRouter text-embedding-3-large or local Ollama). Entity resolution fuses fuzzy matching, vector similarity and an LLM reflection step; a contradiction adjudicator invalidates stale edges instead of stacking them.

text  →  NER (with ontology)
      →  batch embed (text-embedding-3-large | Ollama)
      →  entity resolve (fuzzy + vector + LLM reflection)
      →  MERGE entities (canonical UUIDs)
      →  contradiction adjudication (invalidate old)
      →  CREATE RELATION {valid_at, invalid_at, kind, source_type, source_id}

Retrieval

Each query fans out into three parallel searches - vector edge search (Neo4j HNSW), BM25 fulltext and BFS traversal. Candidates are fused, filtered by valid_at / kind, and re-ranked by a BGE-reranker-v2-m3 cross-encoder.

query
  ├─ vector edge   (Neo4j HNSW)        ─┐
  ├─ BM25 edge     (Neo4j fulltext)    ─┼─ temporal + kind filters → fused top 30
  └─ BFS traversal (from seed entities) ┘
                                       ↓
                BGE-reranker-v2-m3 (MPS / CUDA / CPU)

Communities (zoom-out)

Leiden community detection groups entities into :Community nodes with LLM-written summaries, exposed as the browse_clusters agent tool.

Reasoning memory

Every report persists its full ReACT trace as a traversable subgraph - (:Report)-[:HAS_SECTION]->(:ReportSection)-[:HAS_STEP]->(:ReasoningStep)- so an operator can later ask why the agent concluded X and walk the steps.

What it buys you

Multi-hop queries land facts single-hop RAG misses, temporal queries (as_of="2026-04-10T14:00Z") return the world as known at that moment, and epistemic filtering separates facts from agent beliefs.
Agents

Persona generation & grounding

Each agent is built from five layers of context - graph attributes, relationships, semantic neighbours, BFS context and (optionally) live web research - then anchored to Nemotron demographics.

Five layers of grounding

Each agent fuses five context layers into one persona prompt: node attributes, first-degree relationships, a semantic vector search, BFS neighbours, and - for thin context or public figures - a live web-research call.

Individual vs institutional

Keyword and ontology rules separate a person from an institution and switch the persona schema - institutions speak in press-release voice, individuals in first person.

Web enrichment

Set WEB_SEARCH_MODEL=perplexity/sonar-pro and grounding calls return citation-backed snippets, so a persona reflects recent news rather than a stale training snapshot.

Self-hosted search & scrape (SearXNG / Firecrawl)

MIROSHARK_SEARXNG_BASE_URL routes web-search grounding through your own SearXNG (falling back to WEB_SEARCH_MODEL), and MIROSHARK_FIRECRAWL_BASE_URL scrapes URL inputs through self-hosted Firecrawl - fully local.

Demographic anchoring (Nemotron)

Non-named agents inherit demographics (age, region, education, profession, platform mix) from a Nemotron-aligned population sample, so the swarm mirrors a realistic distribution.
entity ─┬─ attrs          (Neo4j properties)
        ├─ relationships  (1-hop edges)
        ├─ semantic       (vector neighbours)
        ├─ BFS context    (multi-hop)
        └─ web enrichment (public figures only)
                  ↓
        persona prompt  ─▶  Wonderwall agent loop
Engine

Cross-platform simulation engine

Twitter, Reddit and a Polymarket-style prediction market run simultaneously every round. Belief states track stance, confidence and trust; a sliding-window memory keeps prompts compact.

┌─────────────────────────────────────────────┐
│  Round memory (sliding window)              │
│   old: compacted · prev: full · now: live   │
└──┬──────────────┬──────────────┬────────────┘
┌──▼──────┐  ┌────▼────┐  ┌──────▼────────┐
│ Twitter │  │ Reddit  │  │ Polymarket    │
│ posts   │  │ comments│  │ AMM trades    │
└──┬──────┘  └────┬────┘  └──────┬────────┘
┌──▼──────────────▼──────────────▼────────────┐
│  Market-media bridge  (prices ⇄ posts)      │
└──┬──────────────────────────────────────────┘
┌──▼──────────────────────────────────────────┐
│  Belief state (per agent)                   │
│   stance[-1,+1] · confidence[0,1] · trust   │
└─────────────────────────────────────────────┘

Three platforms, one round

All three platforms run concurrently each round via asyncio.gather. Twitter and Reddit generate posts, replies and reactions; Polymarket runs a constant-product AMM with a single LLM-framed market.

Market-Media Bridge

Traders see real posts in their prompt; social agents see market prices in theirs. Cross-platform context is the difference between simulating a crowd and simulating a market.

Prediction market

The market title is generated through the Smart slot - sharp, time-bound and resolvable - and framed at the LLM's own probability estimate. Pricing is a constant-product AMM.

Belief states

Each agent carries a per-topic stance ∈ [-1, +1], a confidence ∈ [0, 1] and a per-agent trust level, updated heuristically each round.

Sliding-window memory

Old rounds are LLM-compacted to summaries on a background thread; the previous round stays full-detail and the current round is live - keeping prompts compact.

Per-round frame API

GET /api/simulation/<id>/frame/<round> returns a compact snapshot - actions, active agents, prices, belief state. Powers the replay scrubber and the CLI frame subcommand.

Performance numbers (vs. naive baseline)

Neo4j writes       1 tx/entity   →  batched UNWIND      (10×)
Chunk + config     sequential    →  parallel executors  (3×)
Memory compaction  blocking      →  background thread
Interaction

Live interaction - Director, chat, forks

Inject breaking news mid-run, fork the timeline with a counterfactual event, or DM any agent. Forks are first-class siblings; comparisons are diffable.

Director Mode (inject)

Drop a breaking-news payload into the current timeline. Active agents observe it next round, beliefs shift and the market reprices - no fork, the same run continues.

Counterfactual Branching (fork)

Pick any round, inject an event, and MiroShark forks the run. The parent keeps running while the child runs independently; the UI shows the two side by side.

Persona chat

Click any agent to open a DM. It answers in character from its current belief state and full posting history. Group sends route the same prompt to a cohort.

Lineage navigator

Every branch knows its parent, siblings and split round. The lineage navigator renders the whole tree so you can compare across counterfactual paths.
Reports

Reports, signals & analytics

A ReACT report agent cites real posts and trades, backed by a trajectory chart, signal JSON, consensus badge, per-run cost and quality diagnostics.

ReACT report agent

A tool-calling loop over simulation_feed, market_state, graph search, belief_trajectory and a Nash equilibrium tool. Every claim is grounded in a citation, and Regenerate Report re-runs the agent and mints a fresh report_id.

Signal JSON & direction

signal.json distils the final belief split into a direction - bullish / bearish / neutral - via a plurality rule with a deterministic tie-break.

Trajectory chart (SVG)

A pure-stdlib SVG of per-round belief means, deltas and confidence bands. Drops straight into oEmbed unfurls and static-site exports - no JS, no PNG roundtrip.

Consensus badge (SVG)

A Shields.io-compatible pill - green / grey / red by direction. One Markdown line turns any README into a live consensus indicator.

Run cost (cost.json)

GET /api/simulation/<id>/cost.json returns a headline estimated_cost_usd plus token / latency totals and by_model / by_phase breakdowns. Flagged is_estimate: true and stated as a lower bound.

Quality diagnostics

A per-run quality score over entity coverage, graph density, agent activity and belief movement - flagging runs where the input was too thin or the swarm was inert.
Inputs

Inputs - Smart Setup, Just Ask, Trending

Drop a doc, type a question, pick from RSS-driven trending news, or use one of nine preset templates - and run the full pipeline in EN / 中 / DE / FR.

Smart Setup

Drop in a document and get three auto-generated Bull / Bear / Neutral scenarios in ~2s. Pick one to seed the run.

Just Ask

Type a question with no document; MiroShark routes through the Smart slot to research and write the seed briefing itself.

Preset templates

Nine benchmarked scenarios - crypto launch, corporate crisis, political debate and more - each tuned for the right preset size, market framing and persona mix.

Locale - EN / 中 / DE / FR

The language selector - EN / 中 / DE / FR - runs the full pipeline natively in the chosen language, not just the UI. API consumers negotiate by precedence: ?lang= > X-MiroShark-Locale > Accept-Language > en.
Models

Model routing - six slots, one key

LLM / Smart / Wonderwall / NER / Embedding / Reranker. One OpenRouter key, five env slots, full freedom to mix providers - including self-hosted vLLM or Claude Code with no API key.

MiroShark splits work across six model slots, so you keep the cheap loop cheap and spend only on the few prompts that steer the run.

LLM (default)

Profiles, sim config, memory compaction. Default: xiaomi/mimo-v2-flash.

Smart

Reports, ontology, graph reasoning, market phrasing. #1 quality lever. Default: google/gemini-3-flash-preview.

Wonderwall

The agent simulation loop. #1 cost driver - use the cheapest viable model. Optionally self-hosted via WONDERWALL_BASE_URL / WONDERWALL_API_KEY.

NER

Entity extraction; needs reliable JSON. Default: google/gemini-3-flash-preview.

Embedding

OpenRouter text-embedding-3-large or a local Ollama endpoint.

Reranker

BGE-reranker-v2-m3 cross-encoder (~1GB one-time download). Runs on Apple MPS, CUDA or CPU.

One key, five slots

Paste a single OpenRouter key into the five API-key env vars (LLM_API_KEY, SMART_API_KEY, NER_API_KEY, OPENAI_API_KEY, EMBEDDING_API_KEY). First run is ~10 min and ~$1.

Claude Code mode

Set LLM_PROVIDER=claude-code to drive Claude through the Code CLI - no API key required.

Local Ollama

Docker + local Ollama, or a fully manual Ollama wiring - both are first-class install paths.

CoT toggle

DISABLE_COT=true on reasoning-capable models drops latency ~3× for slots that don't need it.
Integrations

MCP, webhooks, notifications

MCP server for Claude Desktop / Cursor / Windsurf / Continue. Signed webhooks with delivery log. Channel-native completion notifications on Discord, Slack, Email and Telegram. PWA push.

MCP server

A first-party MCP server exposes the simulation surface to Claude Desktop, Cursor, Windsurf and Continue - spin up sims, fetch frames, query the belief trajectory and invoke the report tools from your editor.

Webhooks

Per-simulation webhooks fire on lifecycle events (start, fork, round, completion, report). Set WEBHOOK_SECRET and every payload is HMAC-signed in the X-MiroShark-Signature header, with an optional WEBHOOK_EVENTS allow-list and a built-in delivery log.

Completion notifications

Opt-in, channel-native alerts on completion: Discord embeds (DISCORD_WEBHOOK_URL), Slack Block Kit (SLACK_WEBHOOK_URL), multipart email (SMTP_HOST + SMTP_TO), Telegram (TELEGRAM_BOT_TOKEN) and PWA browser push - each carrying the stance, belief bars and a deep link.

History database

Local SQLite-backed history of every run, fork, report and chat - source of truth for the gallery, lineage view and per-agent timelines.
Export

Export, embed & provenance

37 published surfaces - provenance (reproduce.json, on-chain DKG, BibTeX), data exports (notebook, archive, clone), oEmbed/Frame v2 auto-unfurl, gallery feeds, and a self-describing /api/surfaces.json catalog.

Provenance

reproduce.json carries every parameter needed to re-run a sim bit-for-bit. Its SHA-256 can be anchored on Base as an OriginTrail DKG Knowledge Asset, and /api/simulation/<id>/cite.bib returns a BibTeX entry carrying the same digest.

Data exports

A per-run notebook.ipynb for slicing the full trajectory in pandas / numpy, a single ZIP archive of every artifact, and /clone.json - inputs wire-compatible with POST /api/simulation/create to re-run the scenario against fresh agents.

Auto-unfurl

One /oembed endpoint unfurls the trajectory chart, headline and consensus badge in Notion, Ghost, Substack and WordPress. The same URL is a valid Farcaster Frame v2, and a pre-formatted X thread ships the run in one paste.

Feeds & live watch

/feed.rss, /feed.atom, /sitemap.xml and /robots.txt make every published sim crawlable and subscribable. A public read-only live watch page broadcasts a run in progress.

Surface catalog (surfaces.json)

GET /api/surfaces.json is a self-describing catalog of all 37 published surfaces - each with its endpoint, method, type, description and a ready-to-run example_curl.

Every export surface is built on pure stdlib and is opt-in via env flag. See the full reference in docs/FEATURES.md.

Ecosystem

Ecosystem & integrators

Projects, agents and tools built on MiroShark - a human-readable ECOSYSTEM.md and a machine-readable /api/ecosystem.json catalog that integrators and registries crawl.

ECOSYSTEM.md (human-readable)

The curated index of projects built on MiroShark - alphabetized, with an “Add your project” PR guide, in EN and 中文. Browse it on the Ecosystem page.

/api/ecosystem.json (machine-readable)

The crawlable counterpart. Each entry carries name, url, description, category, x_handle and repo - ETag-cached and listed in /api/surfaces.json.

Who's building on it

Discovery registries (Sparkleware), synthetic-user research (SyntheticsAI), integration specs (Capacitr), local-first dashboards (HivemindOS) and prediction agents (ZER0) all run real MiroShark simulations.

Drive it from anywhere

Noelclaw ships MiroShark as an MCP server (@noelclaw/mcp) whose miroshark_simulate / miroshark_status tools wrap the full create → start → poll flow. AntFleet runs the first integrator benchmark against the engine.
GET /api/ecosystem.json
{ "success": true,
  "data": {
    "schema_version": "1",
    "count": 14,
    "ecosystem": [
      { "name": "Sparkleware", "category": "integration",
        "x_handle": "…", "repo": "https://github.com/…" },
      …
    ] } }

Add your project with a PR to ECOSYSTEM.md - a square 40 px logo, one-line description, category and links. The machine-readable catalog stays in lockstep via a drift-guard test.

x402

Run MiroShark over x402

x402 turns the dormant HTTP 402 "Payment Required" status into a real payment rail: the server answers a request with a price, the client pays stablecoin over HTTP, and the same request goes through - no account, no API key, no signup. MiroShark is a paid x402 endpoint - your agent POSTs a prompt, pays $1 USDC, and gets back a live URL where a full multi-agent simulation report appears in ~10 minutes.

What you get for $1

The run turns your seed into a knowledge graph, spawns ~25 grounded personas, simulates 10 rounds of Twitter + Reddit + a Polymarket-style prediction market, and returns a cited markdown report - belief drift, top posts, market trajectories, and a knowledge-graph view.

Flat price, one dollar

A flat $1 USDC per run - no metering, no per-token billing, whatever the seed. A real run took ~9 minutes and produced a 37 KB report.

No account, no key

No signup, OAuth, or API key. The only credential is a wallet that can sign a USDC transfer; the returned run_id is the public, unauthenticated token for every follow-up call.

Three chains, one endpoint

The same POST /run takes USDC on Base, Solana, or Monad. The 402 advertises all three as separate accepts[] entries; your client pays whichever chain its wallet holds.
POST /run  ($1 USDC)
   │
   ├─ ingest      seed → entities                  (~8 s)
   ├─ ontology    entity types                     (~10 s)
   ├─ graph_build Neo4j knowledge graph            (~11 s)
   ├─ create      simulation scaffold              (~0 s)
   ├─ prepare     ~25 grounded personas            (~70 s)
   ├─ simulate    10 rounds · Twitter/Reddit/Market (~6-18 min)
   └─ report      cited markdown post-mortem
        │
        └─▶  share_url  (auto-published, recap card, 7 panels)
Endpoint

The /run endpoint - seeds & price

POST /run with exactly one seed - a prompt, an article URL, or raw text. The price is a flat $1.00 USDC, settled on Base, Solana, or Monad - the 402 advertises all three and your wallet picks the chain it holds.

One seed, three shapes

Provide exactly one of these in the JSON body to seed the simulation:
{"prompt":  "<scenario or question, 4-4000 chars>"}      ← most common
{"url":     "<article / news URL - MiroShark fetches & simulates it>"}
{"article": "<raw document text, 4-200000 chars>"}

Price

$1.00 USDC - 6 decimals, so amount: "1000000" atomic units. The x402 v2 exact scheme, settled through the Coinbase CDP facilitator on Base + Solana, and the Monad facilitator on Monad.

No gas needed

The facilitator sponsors settlement gas on all three chains - fund the wallet with ~$1 USDC only, no ETH, SOL, or MON. On Base and Monad you sign an EIP-3009 transferWithAuthorization; on Solana, an SPL Token transfer.

Where the $1 goes - pick the chain your wallet holds

Base mainnet   network  eip155:8453
               USDC     0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
               payTo    0x6cab485fc28ec70d3845113b704d4824e4d2b24f
               amount   "1000000"

Solana mainnet network  solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp
               USDC     EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
               payTo    9vWbPNMvt8ui1cNN8jWWPUWT5LPmeXzq7nr3vry1vMPH
               amount   "1000000"

Monad mainnet  network  eip155:143
               USDC     0x754704Bc059F8C67012fEd69BC8A327a5aafb603
               payTo    0x0000CE08fa224696A819877070BF378e8B131ACF
               amount   "1000000"

Always trust the live 402 response's PAYMENT-REQUIRED header (base64 JSON, field accepts[]) over anything hardcoded - the values above are just a head-start so you can pre-fund the right wallet.

Protocol

The x402 payment flow

The standard x402 v2 flow: POST, 402 with a PAYMENT-REQUIRED header, sign the USDC transfer, re-POST with PAYMENT-SIGNATURE, 202 Accepted with your run_id and follow URLs. Gas is sponsored by the facilitator (CDP for Base + Solana, the Monad facilitator for Monad).

1. POST /run  (no payment)
        ↓
2. 402 Payment Required  + PAYMENT-REQUIRED header (base64 accepts[])
        ↓
3. client decodes accepts[], signs the USDC transfer authorization
        ↓
4. POST /run again  + PAYMENT-SIGNATURE header (the signed payload), SAME body
        ↓
5. server settles on-chain → 202 Accepted + PAYMENT-RESPONSE (tx hash)

What a v2 SDK does for you

An x402-aware client does every step above automatically: it catches the 402, decodes accepts[], signs against the matching chain, and replays the request with PAYMENT-SIGNATURE. You write one POST and read the 202.

The 202 response body

On success you get a run_id plus two ways to follow the run; the PAYMENT-RESPONSE header carries the on-chain tx hash for your records.
HTTP/2 202 Accepted
PAYMENT-RESPONSE: <base64 SettleResponse - tx hash + payer + network>

{
  "success": true,
  "data": {
    "run_id":    "run_86ead0ea7fa7",
    "status":    "queued",
    "stages":    ["ingest","ontology","graph_build","create","prepare","simulate","report"],
    "wait_url":  "https://x402.miroshark.xyz/wait/run_86ead0ea7fa7",
    "status_url":"https://x402.miroshark.xyz/status/run_86ead0ea7fa7",
    "payer":     "0xYourWalletAddress",
    "payment_chain":   "base",
    "payment_network": "eip155:8453",
    "cancel_token": "q3V9...kZ0"
  }
}

Keep cancel_token private. It is the only way to cancel the run, it appears only in this response (and in an idempotent replay of it), and the server stores just a hash.

Clients

Pay from any runtime

Drop-in code for the official x402 SDK in Python (Base, Solana + Monad) and TypeScript, the awal CLI for shell agents, the payments MCP for Claude Desktop / ChatGPT, and an ERC-7710 sidecar for MetaMask Smart Accounts.

Python - Base (EVM), official x402 SDK

pip install 'x402[evm,requests]' eth-account requests
import os
from eth_account import Account
from x402.client import x402ClientSync
from x402.http.clients import x402_requests
from x402.mechanisms.evm.exact.client import ExactEvmScheme

account = Account.from_key(os.environ["X402_BUYER_PRIVATE_KEY"])

client = x402ClientSync()
client.register("eip155:8453", ExactEvmScheme(signer=account))   # Base mainnet
session = x402_requests(client)   # handles 402 → sign → retry automatically

resp = session.post(
    "https://x402.miroshark.xyz/run",
    json={"prompt": "How will developers react if OpenAI releases GPT-6 next month?"},
    timeout=120,
)
data = resp.json()["data"]
print("Run ID:", data["run_id"], "| follow:", data["wait_url"])

Fund the address with ~$1+ USDC on Base mainnet - no ETH needed, gas is sponsored.

Python - Solana (SVM)

Same flow, different scheme - install the svm extra and register the Solana scheme; the adapter does 402 → sign SPL transfer → retry.
pip install 'x402[svm,requests]'
import os, json
from solders.keypair import Keypair
from x402.client import x402ClientSync
from x402.http.clients import x402_requests
from x402.mechanisms.svm.exact.client import ExactSvmScheme
from x402.mechanisms.svm.signers import KeypairSigner

SOLANA_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"

raw = os.environ["SOLANA_BUYER_PRIVATE_KEY"].strip()
kp = (Keypair.from_bytes(bytes(json.loads(raw))) if raw.startswith("[")
      else Keypair.from_base58_string(raw))

client = x402ClientSync()
client.register(SOLANA_MAINNET, ExactSvmScheme(signer=KeypairSigner(kp)))
session = x402_requests(client)   # picks the Solana accepts[] entry automatically

resp = session.post(
    "https://x402.miroshark.xyz/run",
    json={"prompt": "Your scenario"}, timeout=120,
)
print(resp.json()["data"]["run_id"])   # paid on: "solana"

Python - Monad (EVM), official x402 SDK

Monad is EVM, so it is the exact same flow as Base - just register the eip155:143 network. Use x402[evm] >= 2.22.0 (built-in Monad mainnet USDC).
import os
from eth_account import Account
from x402.client import x402ClientSync
from x402.http.clients import x402_requests
from x402.mechanisms.evm.exact.client import ExactEvmScheme

account = Account.from_key(os.environ["X402_BUYER_PRIVATE_KEY"])

client = x402ClientSync()
client.register("eip155:143", ExactEvmScheme(signer=account))   # Monad mainnet
session = x402_requests(client)   # picks the Monad accepts[] entry automatically

resp = session.post(
    "https://x402.miroshark.xyz/run",
    json={"prompt": "Your scenario"}, timeout=120,
)
print(resp.json()["data"]["run_id"])   # paid on: "monad"

Fund the address with ~$1+ USDC on Monad mainnet - no MON needed, gas is sponsored by the Monad facilitator.

TypeScript / JavaScript

npm install @x402/fetch @x402/evm viem
import { x402Client } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";

const client = new x402Client();
const signer = privateKeyToAccount(process.env.X402_BUYER_PRIVATE_KEY as `0x${string}`);
registerExactEvmScheme(client, { signer });

const res = await client.fetch("https://x402.miroshark.xyz/run", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ prompt: "Your scenario" }),
});
const data = (await res.json()).data;

Shell / CLI agents

Claude Code, Codex CLI, Gemini CLI - use the Agentic Wallet CLI, which handles sign-in, funding and the dance:
npx awal x402 pay \
  https://x402.miroshark.xyz/run \
  --body '{"prompt":"Your scenario"}'

MCP-only hosts

Claude Desktop, ChatGPT, Cherry Studio - install @coinbase/payments-mcp once, restart the host, then call make-x402-request with the URL + JSON body.

MetaMask Smart Accounts

For delegated / recurring budgets, an ERC-7710 sidecar takes the same dance at POST /run-7710 - grant an ERC-7715 permission once, then a session key pays per run inside it.

Any x402 v2 client works - these are the common ones. To mint a throwaway EVM wallet locally (the key never leaves your machine): python -c "from eth_account import Account; a=Account.create(); print(a.address, a.key.hex())", then fund it with USDC on Base.

Inputs

Tuning the run

Two optional fields ride along with any seed: prediction_market to pin the central market. A live-web deep-research sweep runs automatically for prompt seeds.

deep research - automatic for prompt seeds

A bare prompt has no source text to ground on, so MiroShark automatically runs a multi-query live-web sweep before the sim, synthesizing a richer, current seed with named actors. url and article seeds already carry source text, so they skip it. It is decided server-side from the seed - not a request field.
{"prompt": "EU AI Act enforcement in 2026"}
// → live-web deep research runs automatically

prediction_market - pin the market

By default MiroShark designs the Polymarket question for you. Pass a bare string to force the central market, or an object to set the opening price and outcome labels.
{"prompt": "...",
 "prediction_market": {
   "question": "Will Argentina win the 2026 FIFA World Cup?",
   "outcome_a": "YES", "outcome_b": "NO",
   "initial_probability": 0.28
 }}
Polling

Follow the run & read the report

Reading needs only the run_id - no auth. Poll /status for JSON, open /wait for an auto-refreshing page, or pull the finished report as raw markdown or JSON from /report. Cancelling needs the private cancel_token from the /run response. Every run auto-publishes a shareable recap card.

status_url - JSON, for agents

GET /status/<run_id> - no auth, structured for polling. Poll until status is terminal; on completed, share_url is the absolute share-page URL.

wait_url - HTML, for humans

GET /wait/<run_id> - a public page that auto-refreshes every 5 s, shows the live stage + progress bar, and links to /share/<sim_id> once the report renders. Close the tab and the run continues server-side.

The status payload (declared in /openapi.json)

GET /status/<run_id>
{
  "success": true,
  "data": {
    "run_id": "run_86ead0ea7fa7",
    "status": "running",        // queued | running | completed | failed | budget_exceeded | cancelled
    "progress": 42,             // 0-100
    "current_stage": "simulate",
    "current_round": 4,
    "message": "Round 4/10",
    "simulation_id": null,      // set on completion
    "share_url": null,          // set on completion → https://<host>/share/<sim_id>
    "budget": {"cost_usd": 0.11, "tokens_used": 184223, "calls": 57},
    "payment_chain": "solana",
    "payment_network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
  }
}

Poll until status is completed, failed, budget_exceeded or cancelled. Note stages here is an object keyed by stage name, whereas the stages in the 202 body is an array - don't reuse one parser for both.

Cancel a run

POST /cancel/<run_id>
X-Cancel-Token: <cancel_token from the /run response>

202 {"success": true, "data": {"run_id": "run_86ead0ea7fa7", "status": "cancelled", "signaled": true}}

The run_id alone is not enough: run ids appear in share links and the public grid. Idempotent. Responses: 409 already_terminal if the run already finished, 401 cancel_token_required without the header, 403 invalid_cancel_token for a wrong token. Cancelling early stops LLM spend, so more of the $1 stays as your builder share.

Read the report without the website

Once completed, pull the report text directly - no HTML, no auth:
GET /report/<run_id>?format=md     → raw markdown
GET /report/<run_id>?format=json   → {report_markdown, title, share_url, …}
Before the run finishes these return 409 with the current status.

The recap card (auto-unfurl)

Every completed run produces a 1200×630 PNG - the image that unfurls on X / Discord / Slack / iMessage. Grab it directly:
GET /api/simulation/<sim_id>/share-card.png
Generated on first request and cached, so it's cheap to hot-link.
Free

Pre-flight & health checks

Three unpaid helpers: /suggest turns a vague topic into launchable prompts before you spend a cent, and /health + /ready tell you the service is up so your settle doesn't fail.

/suggest - ideas before you spend

Not sure what to run? POST /suggest (free, no payment) turns a vague topic into up to 5 launchable ideas, each with a ready-to-run prompt for /run. Rate-limited per IP and cached; returns an empty list on a transient blip rather than erroring.
POST /suggest   {"prompt": "stablecoin regulation"}
→ {"data": {"ideas": [{title, pitch, prompt, angle}, …]}}

/health & /ready - is it up?

Check readiness before you pay - if /ready is 503, your settle will probably fail.
GET /health   → liveness, no downstream checks
GET /ready    → 200 when DB + Neo4j are reachable; 503 otherwise
There are no chargebacks, so waiting for a green /ready is the cheapest insurance you have.
Discovery

Discover & verify the service

MiroShark is cataloged across the main x402 directories - CDP Bazaar, agentic.market, x402scan, AgentCash, Ampersend - so an agent can find and vet it by semantic search before ever making a paid call.

Listed as “MiroShark”

Tagged Simulation, Research, Search, Crypto, AI, with a logo at /logo.png. An agent-facing manual for the full suggest → pay → poll → report loop is served at GET /skill.md.

Pick before paying

Each directory exposes the same accepts[] shape as the live 402 plus the input/output schemas - so an agent can choose MiroShark by semantic search and code against it without a paid request first.

Where MiroShark indexes

  • CDP Bazaar - indexes on each successful settle from the service metadata + bazaar schemas.
  • agentic.market - mirrors CDP Bazaar automatically.
  • x402scan & AgentCash - read GET /openapi.json (OpenAPI 3.1) as the canonical contract.
  • Ampersend marketplace - curated catalog; maps /skill.md + /logo.png to its skillmd_url / logo_url fields.
# CDP discovery is read-only and unauthenticated:
curl 'https://api.cdp.coinbase.com/platform/v2/x402/discovery/search?query=miroshark'
curl 'https://api.cdp.coinbase.com/platform/v2/x402/discovery/merchant?payTo=0x6cab485fc28ec70d3845113b704d4824e4d2b24f'
Verify the live listing
Reference

Wire-format reference

If you don't use a v2-aware SDK, here are the exact bytes: the base64 PAYMENT-REQUIRED challenge with all three accepts[] entries, the PAYMENT-SIGNATURE payload shape, and the PAYMENT-RESPONSE that carries your tx hash.

1 · The 402 challenge (decoded PAYMENT-REQUIRED)

{
  "x402Version": 2,
  "resource": {
    "url": "https://x402.miroshark.xyz/run",
    "serviceName": "MiroShark",
    "tags": ["Simulation","Research","Search","Crypto","AI"]
  },
  "accepts": [
    { "scheme":"exact", "network":"eip155:8453",
      "asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "amount":"1000000",
      "payTo":"0x6cab485fc28ec70d3845113b704d4824e4d2b24f",
      "maxTimeoutSeconds":300, "extra":{"name":"USDC","version":"2"} },
    { "scheme":"exact", "network":"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "asset":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      "amount":"1000000",
      "payTo":"9vWbPNMvt8ui1cNN8jWWPUWT5LPmeXzq7nr3vry1vMPH",
      "maxTimeoutSeconds":300, "extra":{"feePayer":"<facilitator-sponsored>"} },
    { "scheme":"exact", "network":"eip155:143",
      "asset":"0x754704Bc059F8C67012fEd69BC8A327a5aafb603",
      "amount":"1000000",
      "payTo":"0x0000CE08fa224696A819877070BF378e8B131ACF",
      "maxTimeoutSeconds":300, "extra":{"name":"USDC","version":"2"} }
  ]
}
Three entries - Base (EVM), Solana, and Monad (EVM) - all $1 USDC. Pick the one matching your wallet's chain.

2 · Retry with PAYMENT-SIGNATURE (Base / EVM example)

Sign an EIP-3009 transferWithAuthorization for amount of the USDC asset to payTo; base64-encode the PaymentPayload into the PAYMENT-SIGNATURE header with the same body.
{
  "x402Version": 2,
  "accepted": { … the chosen accepts[] entry … },
  "resource": "https://x402.miroshark.xyz/run",
  "payload": {
    "signature": "0x…",
    "authorization": {
      "from": "0xYourWalletAddress",
      "to":   "0x6cab485fc28ec70d3845113b704d4824e4d2b24f",
      "value":"1000000",
      "validAfter":"…", "validBefore":"…",
      "nonce":"0x<random 32 bytes>"
    }
  }
}
On Solana you build and sign an SPL Token transfer of the same amount, leaving feePayer to the facilitator - the SDK's SVM scheme handles it.

3 · The settle (decoded PAYMENT-RESPONSE)

{
  "success": true,
  "transaction": "0x1f2ab48e…2ee06a",   // on-chain USDC transfer
  "network": "eip155:8453",
  "payer": "0xYourWalletAddress"
}
Verify the transaction on BaseScan (Base) or Solscan (Solana - a base58 signature), or MonadVision (Monad). The same payment_chain / payment_network ride along in the 202 body and every /status response.

Full reference: the x402 v2 specification.

Caveats

Caveats - finality, refunds & errors

x402 is final - once USDC settles there is no chargeback. Design retries that never double-pay, know what 402 / 429 / 404 each mean, and understand there is no automatic refund if a paid run fails.

No chargebacks - ever

Once your USDC settles, the operator has it; x402 has no chargebacks. If the PAYMENT-SIGNATURE POST errors mid-flight, check the chain explorer for your signed nonce (EVM) or signature (Solana) before retrying so you don't double-pay.

No automatic refund on failure

If a run shows failed or budget_exceeded after settle, the $1 is already paid - there's no auto-refund. Contact the operator with your run_id for a manual review.

Runs take 15-20 min

The simulate stage is ~6-18 min on its own. A 429 means the server hit its concurrency cap - retry with backoff. Polling is free, so poll /status rather than re-POSTing.

404 on status_url

Means the run_id is wrong or you polled a redeployed instance with a wiped DB. Re-POST /run only if you've confirmed on BaseScan/Solscan that the original payment never settled.

402 returned again after you sent PAYMENT-SIGNATURE?

Usually insufficient USDC, a signature mismatch, a reused nonce (EVM) or a stale blockhash (Solana). Check the balance on BaseScan / Solscan, mint a fresh nonce, confirm the EIP-712 domain matches extra.name / extra.version, and make sure you signed the right chain's accepts[] entry.

Heads-up for pay / pay.sh users: the CLI only routes x402 for endpoints in its catalog. MiroShark's listing is pending; until it merges, pay falls back to MPP and won't settle here - use any direct x402 client above.

x402aff

Revenue share - the x402aff kit

MiroShark pays the apps that drive paying users a cut - 10% of each $1 run by default - enforced on-chain at settlement. It runs on the open-source x402aff kit (MIT, on npm + PyPI as x402aff, repo github.com/MiroShark/x402aff), not a bespoke MiroShark mechanism, so any x402 seller can pay their own builders the same way.

Earn a cut - attach a Base Builder Code (builder side)

Attach a Base Builder Code to the x402 client you pay MiroShark with, and every run you drive is attributed to your app on-chain. MiroShark routes your share - 10% of each $1 payment by default - into a per-builder split at settlement. Get a code at base.dev → Settings → Builder Codes.
npm install @x402/extensions

import { BuilderCodeClientExtension } from "@x402/extensions/builder-code";

// on the same x402Client() you registered the scheme on, before paying:
client.registerExtension(new BuilderCodeClientExtension("bc_yourcode"));
// every payment you drive now carries your code on-chain

Where the money goes

Your cut lands in an ownerless, immutable 0xSplits contract at settlement, enforced on-chain - nobody, MiroShark included, can redirect it or change the share. Releasing the funds (distribute) is permissionless: anyone can trigger the payout, and the money only ever reaches the builder and the seller, so whoever clicks pays the gas and receives nothing extra.

Run it on your own API (seller side)

None of this is MiroShark-specific. It is powered by the open-source x402aff kit (MIT), and any x402 seller can pay their own builders the same way. x402aff deploys no contracts of its own - it reads the Base Builder Codes registry and lets the stock CDP facilitator settle into an audited 0xSplits contract, so there is no facilitator to run and no key to hold.
npm install x402aff viem     # or: pip install x402aff

import { Affiliation } from "x402aff";

const aff = new Affiliation({ appCode: "bc_yourcode", sellerPayout: "0x…" });

// aff.payTo is a drop-in x402 DynamicPayTo callback:
paymentMiddleware(facilitator, {
  "/api/data": { payTo: aff.payTo, extensions: aff.extensions },
});

Set your route's payTo to aff.payTo and each payment settles into a per-builder split instead of your wallet. An unknown or unresolvable code falls back to your wallet, so a payment never fails - it just isn't split. Python is the same shape: pip install x402aff, then Affiliation(app_code="bc_yourcode", seller_payout=…).

Live dashboard

The x402aff dashboard is this kit running live against MiroShark - every split, balance and claim on it comes straight from the kit. Its All sellers view lists every split the kit has routed on Base, whoever runs it.

Run it

Your first simulation takes ~10 min and ~$1.

One OpenRouter key, one launcher. Clone, drop the key into the five slots, run ./miroshark, openlocalhost:3000.