MiroShark logoMiroShark
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.

00Overview

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. NER uses few-shot examples plus rejection rules to filter garbage. Chunks are processed in parallel and written in batched UNWIND transactions - ~10× faster than per-entity writes.

2 · Agent setup

Personas are generated from the graph. Each gets five layers of context: attributes, relationships, semantic search, BFS neighbours, and (for public figures or thin context) a live web research call.

3 · Simulation

Twitter, Reddit and Polymarket run simultaneously via asyncio.gather. A market-media bridge lets traders read posts and lets social agents see prices. Beliefs and trust update each round.

4 · Report

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

5 · Interaction

DM any agent, send a question to a group, inject breaking news, or fork the timeline with a counterfactual event. Forks are siblings; the UI diffs them.

Output surfaces

Every run yields a citing report, a trajectory chart, a signal JSON and badge, a tweet-thread export, a Jupyter notebook, an oEmbed/Frame v2 unfurl, a reproducibility bundle, and an optional DKG anchor on Base - 37 surfaces in all, each listed in /api/surfaces.json.
document  ─▶  graph build  ─▶  persona grounding  ─▶  3× platform loop  ─▶  report
                                                       │
                                                       ├─ Director Mode  (inject)
                                                       ├─ Fork           (branch)
                                                       └─ Persona Chat   (DM)
01Memory

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 (with an ontology) into a batched embedding call (OpenRouter text-embedding-3-large or a local Ollama equivalent). Entity resolution combines fuzzy matching, vector similarity, and an LLM reflection step - collapsing “NeuralCoin”, “Neural Coin” and “NC” into one canonical UUID. Same-endpoint relationship pairs are sent to an LLM adjudicator that invalidates the older edge instead of stacking contradictions.

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 over Neo4j HNSW, BM25 fulltext, and a BFS traversal from seed entities. The top 30 candidates are fused, filtered by valid_at / kind, and re-ranked by a BGE-reranker-v2-m3 cross-encoder on Apple MPS, CUDA or CPU.

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)
                                       ↓
                top N tagged with _sources ("v" / "k" / "g" / combos)

Communities (zoom-out)

Leiden community detection (via igraph) groups entities. An LLM writes a 2-sentence summary and title per cluster; clusters persist as :Community nodes with MEMBER_OF edges. Semantic search over cluster summaries is exposed as the browse_clusters agent tool.

Reasoning memory

Every report generation persists its full ReACT trace as a traversable subgraph: (:Report)-[:HAS_SECTION]->(:ReportSection)-[:HAS_STEP]->(:ReasoningStep). Step kinds are thought, tool_call, observation, conclusion - so a future operator can ask “why did the agent conclude X?” and walk the trace.

What it buys you

Multi-hop queries land facts that single-hop RAG misses. Temporal queries (as_of="2026-04-10T14:00Z") return the world as known at that moment. Epistemic filtering separates ground-truth facts from agent beliefs. First-call recall is high enough that the report agent's 5-call budget actually goes somewhere.
02Agents

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

For every entity that becomes an agent, the generator pulls: (1) the node's attributes, (2) its first-degree relationships, (3) a semantic vector search over the rest of the graph, (4) related nodes via BFS, and (5) - when the graph context is thin (< 150 chars) or the entity looks like a public figure - a live web-research LLM call. The five layers fuse into a single persona prompt.

Individual vs institutional

The system distinguishes a person (“Vitalik Buterin”) from an institution (“Ethereum Foundation”) via keyword and ontology rules, and switches the persona schema accordingly - institutions speak in press-release voice, individuals in first person with personality variance.

Web enrichment

When enabled, set WEB_SEARCH_MODEL=perplexity/sonar-pro in .env. The grounding call goes through OpenRouter and returns citation-backed snippets that get folded into the prompt - so a CEO persona reflects the actual last-week-of-news version of the CEO, not a stale training snapshot.

Self-hosted search & scrape (SearXNG / Firecrawl)

For LLMs without native web search, point enrichment at a self-hosted stack instead of a hosted model. MIROSHARK_SEARXNG_BASE_URL routes web-search grounding through your own SearXNG instance - it takes precedence over WEB_SEARCH_MODEL and falls back to it on failure - while MIROSHARK_FIRECRAWL_BASE_URL / MIROSHARK_FIRECRAWL_API_KEY scrape URL inputs through a self-hosted Firecrawl. Fully local, no third-party search API.

Demographic anchoring (Nemotron)

Non-named agents inherit demographic features (age bracket, region, education, profession, online platform mix) from a Nemotron-aligned population sample. The result is a swarm that mirrors a realistic population distribution rather than a sea of identical Reddit-shaped archetypes.
entity ─┬─ attrs            (Neo4j properties)
        ├─ relationships   (1-hop edges)
        ├─ semantic        (vector neighbours)
        ├─ BFS context     (multi-hop)
        └─ web enrichment  (Perplexity / Sonar - public figures only)
                  ↓
        persona prompt  ─▶  Wonderwall agent loop
03Engine

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 rounds:        LLM-compacted summaries              │
│  Previous round:    full action detail                   │
│  Current round:     live (partial)                       │
└────┬─────────────┬─────────────┬─────────────────────────┘
     │             │             │
┌────▼────┐   ┌────▼────┐   ┌────▼──────────┐
│ Twitter │   │ Reddit  │   │ Polymarket    │
│ Posts   │   │ Comments│   │ Trades (AMM)  │
│ Likes   │   │ Upvotes │   │ Single market │
│ Reposts │   │ Threads │   │ Buy/Sell/Wait │
└────┬────┘   └────┬────┘   └────┬──────────┘
     │             │             │
┌────▼─────────────▼─────────────▼─────────────────────────┐
│        Market-Media Bridge                               │
│  Social sentiment   → trader prompts                     │
│  Market prices      → social media prompts               │
│  Social posts       → trader observation                 │
└────┬─────────────┬─────────────┬─────────────────────────┘
     │             │             │
┌────▼─────────────▼─────────────▼─────────────────────────┐
│        Belief State (per agent)                          │
│  Positions:   topic → stance (-1 to +1)                  │
│  Confidence:  topic → certainty (0 to 1)                 │
│  Trust:       agent → trust level (0 to 1)               │
└──────────────────────────────────────────────────────────┘

Three platforms, one round

All three platforms execute 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 actual posts in their observation 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 model slot - phrasing has to be sharp, time-bound and resolvable, because it frames the entire run. Initial price is the LLM's probability estimate (non-50/50). Pricing is constant-product AMM.

Belief states

Each agent carries a per-topic stance ∈ [-1, +1], a confidence ∈ [0, 1], and a per-agent trust level. Heuristic updates each round capture exposure, agreement, and trusted-source weighting.

Sliding-window memory

Old rounds are LLM-compacted to summaries on a background thread. Previous round stays as full detail; current round is live. The window keeps prompts compact while preserving narrative continuity.

Per-round frame API

GET /api/simulation/<id>/frame/<round> returns a compact snapshot - actions, active-agent count, market prices, belief state. Query params: platforms, include_belief, include_market. Powers the replay scrubber and the CLI frame subcommand.

Performance numbers (vs. naive baseline)

Neo4j writes        1 tx per entity      →  batched UNWIND      (10×)
Chunk processing    sequential           →  ThreadPoolExecutor (3×)
Config generation   sequential batches   →  parallel batches   (3×)
Platform execution  partial concurrency  →  all 3 in parallel
Memory compaction   blocking             →  background thread
04Interaction

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 in their next round, beliefs shift, the market reprices. No fork - the same run continues with new information.

Counterfactual Branching (fork)

Pick any round, inject an event (“CEO resigns in round 24?”), and MiroShark forks the simulation. The parent keeps running; the child runs independently. The UI shows the two side-by-side so you can read the divergence.

Persona chat

Click any agent to open a DM. The chat uses the agent's current belief state and full posting history - it answers in character, not as a stateless LLM. Group sends route the same prompt to a selected cohort.

Lineage navigator

Forks aren't flat: every branch knows its parent, its sibling branches, and the round at which it split. The lineage navigator renders the whole tree so you can compare across counterfactual paths, not just two-by-two.
05Reports

Reports, signals & analytics

A ReACT report agent cites real posts and trades. Trajectory chart, signal JSON, peak-round analytics, per-agent sparklines, consensus badge, Nash equilibrium tool.

ReACT report agent

The report agent runs a tool-calling loop with simulation_feed (real posts / comments / trades), market_state (prices, P&L), graph search, belief_trajectory, and a Nash equilibrium tool. Every claim is grounded in a citation. A Regenerate Report button re-runs the agent on demand and mints a fresh report_id.

Signal JSON & direction

signal.json distils the final belief split into a direction - bullish > bearish > neutral - using a plurality rule with a deterministic tie-break. Same rule drives the platform aggregate.

Trajectory chart (SVG)

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

Consensus badge (SVG)

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

Peak-round analytics

Identifies the round where belief variance, market velocity or comment volume peaks. Lets a reader jump straight to the inflection moment instead of scrubbing the whole timeline.

Belief volatility

/volatility is the turbulence counterpart to peak-round - it describes the distribution of round-over-round belief swings: mean_delta_pct, std_dev_delta_pct, a volatility_index (0–100) and a trend of stable / converging / contested.

Per-agent sparklines

One inline SVG per agent shows that agent's belief trajectory across the run. Click through to the full persona profile + simulation history.

Agent roster (agents.json)

/agents.jsonis the “who they were” export - name, bio, a truncated persona preview, demographics (age / gender / MBTI / country / profession / interests), karma, and each agent's final stance and rounds participated. Sparklines show how a belief moved; this shows whose it was.

Polymarket-ready prediction

/polymarket.json is the first integrator-shaped surface: a direction-aware yes_probability / no_probability pair (summing to 1.0), a four-bucket confidence_tier, risk tier and a suggested market title. Emits only once a run is completed - a stricter gate than signal.json.

Platform & project stats

/api/stats aggregates consensus distribution, average confidence and surface views across every public completed sim, with a Shields.io /api/stats/badge.svg pill. /api/project/<id>/stats scopes the same numbers to one workspace and adds a per-project quality_distribution.

Run cost (cost.json)

GET /api/simulation/<id>/cost.json makes the “simulate anything for ~$1” claim queryable per run: a headline estimated_cost_usd plus token / latency totals and by_model / by_phase breakdowns, priced off the same OpenRouter table that writes run_summary.md. It is flagged is_estimate: true and stated as a lower bound - untracked models count as $0, never hidden. The public embed widget now renders it as a ~$0.87 pill, so a stranger sees the real number rather than a marketing line.

Predictive accuracy ledger

Cross-references published predictions with later ground truth, so the system tracks (and exposes) how often the swarm was right.

Quality diagnostics

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

Inputs - Smart Setup, Just Ask, Trending

Drop a doc, type a question, pick from RSS-driven trending news, or use one of six preset templates. Shareable links land readers on a pre-filled form.

Smart Setup

Drop in a document → 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

Six benchmarked scenarios - crypto launch, corporate crisis, political debate, product announcement, campus controversy, historical what-if - each tuned for the right preset size, market framing and persona mix.

Live Oracle data (FeedOracle MCP)

Opt-in. 484 grounded feed tools - markets, on-chain data, news, social - exposed via the FeedOracle MCP. Seeds get anchored in current state, not in stale training data.

Per-agent MCP tools

Personas can invoke real MCP tools during the simulation - web search, on-chain reads, your in-house APIs. The Wonderwall loop treats them as first-class actions alongside posting and trading.

Locale - EN / 中 / DE / FR

A top-right language selector - EN / 中 / DE / FR - persists per-browser and is reflected in the public gallery cards. All four ship full prompt locales, so the agent loop, persona generation, NER / ontology and the report agent run natively in the chosen language - with reinforcement that keeps agents from drifting back to English or Chinese mid-run - not just the UI chrome. API consumers negotiate en / zh-CN / de / fr over HTTP by precedence: ?lang= > X-MiroShark-Locale > Accept-Language > en - localizing error messages, template metadata, feed copy and report narration.
07Models

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 independent model slots, so you can keep the cheap loop cheap and spend on the few prompts that actually steer the run.

LLM (default)

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

Smart

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

Wonderwall

The agent simulation loop - the most expensive surface by token volume. #1 cost driver, use the cheapest viable model. Optionally point at a self-hosted vLLM, Modal endpoint, or fine-tune via WONDERWALL_BASE_URL / WONDERWALL_API_KEY.

NER

Entity extraction. Needs reliable JSON and no hidden chain-of-thought. Default: google/gemini-3-flash-preview.

Embedding

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

Reranker

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

One key, five slots

The recommended path is a single OpenRouter key pasted 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 and MiroShark drives 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=trueon reasoning-capable OpenRouter models (Qwen3-Flash, Gemini-3-Flash) drops latency by ~3× for slots that don't need it.
08Integrations

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 (VS Code / JetBrains). Spin up sims, fetch frames, query the belief trajectory, and invoke the report agent's tools - all from your editor.

Webhooks

Per-simulation webhooks fire on lifecycle events (run start, fork, round, completion, report). Set WEBHOOK_SECRET and every payload is HMAC-signed in the X-MiroShark-Signature header (Stripe / GitHub scheme). Event filtering and a delivery log come built-in.

Webhook event filter

WEBHOOK_EVENTS is an optional comma-separated allow-list that filters completion webhooks before dispatch (blank = fire on everything). Tokens combine OR within a category and AND across them - e.g. bullish,bearish,high_confidencemeans “(bullish OR bearish) AND high confidence.” simulation.failed always bypasses the filter.

WaybackClaw archive

The IPFS + Nostr sibling of the DKG citation. One opt-in POST to api.waybackclaw.space pins the run snapshot (scenario, agent count, consensus, quality, lineage, reproduce.json SHA-256) to IPFS and broadcasts a NIP-01 note to Nostr relays. Free, no on-chain cost - run it alongside DKG for triple-redundant provenance. Configure with WAYBACKCLAW_AGENT_TOKEN.

Discord

DISCORD_WEBHOOK_URL → MiroShark posts a native rich-embed: stance-coloured side border, scenario title, belief-percent fields, share-card thumbnail, deep link. Pure stdlib, opt-in, fire-and-forget.

Slack

SLACK_WEBHOOK_URL→ a native Block Kit message with title block, Unicode block-character belief bars, quality / scale / outcome fields, and an “Open simulation” action button.

Email (SMTP)

SMTP_HOST + comma-separated SMTP_TO → a multipart/alternative email per completion. Subject is [MiroShark] Bullish: <scenario>, so a single Gmail filter routes by direction. STARTTLS on 587; STARTTLS-failure with credentials refuses to send cleartext.

Telegram

TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID→ a Bot-API message with HTML parse mode, a stance-coloured headline, Unicode belief bars, and an inline “View simulation” button.

PWA push

Browser push notifications via the PWA shell - opt-in per device, fires on completion and on Director-Mode events you subscribed to.

History database

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

Export, embed & provenance

37 published surfaces - reproducibility JSON, BibTeX, Jupyter, per-run cost, oEmbed/Frame v2, archive bundle, clone inputs, a self-describing /api/surfaces.json catalog - plus on-chain DKG and WaybackClaw IPFS/Nostr provenance.

Reproducibility JSON

reproduce.json carries every parameter - seed, model slots, persona list, prediction-market framing - needed for a second operator to re-run the simulation bit-for-bit. The canonical bytes are stable.

Clone inputs (clone.json)

The first share surface that returns a sim's inputs rather than its outputs. /clone.json ships a clone_payload that is wire-compatible with POST /api/simulation/create - drop it straight back in to spin up the same scenario against fresh agents.

On-chain provenance (DKG)

The OriginTrail DKG citation anchors reproduce.json's SHA-256 on Base as a Knowledge Asset. A reviewer can fetch the UAL years later and verify the local file still hashes to the recorded digest - DOI-grade provenance, no publishing-house intermediary.

BibTeX

GET /api/simulation/<id>/cite.bib returns a @misc{…}entry that drops straight into a LaTeX source or imports cleanly into Zotero / Mendeley via “Import from URL”. The note field carries the reproduce.json SHA-256 and the annote carries the DKG UAL when present.

Jupyter notebook

A notebook.ipynbper run drops the full trajectory - beliefs, market series, action stream - into a researcher's IDE for direct slicing in pandas / numpy.

Archive bundle

A single ZIP capturing every artifact in one download: report, reproduce.json, notebook, badge SVGs, trajectory SVG, share card, DKG citation, transcript. Cold-storage-friendly.

Tweet thread export

A pre-formatted X / Twitter thread with the headline takeaway, peak rounds, belief deltas and the share card. Built so an operator can ship the run to their audience in one paste.

oEmbed auto-unfurl

One /oembed endpoint covers Notion, Ghost, Substack and WordPress. Paste the sim URL into any of them and the embed renders the trajectory chart, headline, and consensus badge inline.

Farcaster Frame v2

The same sim URL is a valid Frame v2. Cast it on Farcaster and the recipient gets an interactive belief-trajectory card with deep links back to the live run.

Live watch page

A spectator broadcast surface: a public, read-only live view of a running simulation, suitable for sharing during press cycles or streamed events.

Surface catalog (surfaces.json)

The surface that lists the surfaces. GET /api/surfaces.json is a self-describing catalog of all 37 published surfaces on a deployment - each with its endpoint, method, type (analytics / visualization / export / embed / integration / platform / discovery), description and a ready-to-run example_curl. An integrator answers “what can this host do?” with one call.

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

10Ecosystem

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, agents and products built on top of MiroShark - alphabetized, with a logo column and an “Add your project” PR guide. Linked from the README in both EN and 中文. Browse it on the Ecosystem page.

/api/ecosystem.json (machine-readable)

The crawlable counterpart. Each entry carries name, url, description, category (product / tool / integration / agent / benchmark), x_handle and repo. ETag-cached and exposed as a discovery surface in /api/surfaces.json.

Who's building on it

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

Drive it from anywhere

Noelclaw ships MiroShark as an MCP server (@noelclaw/mcp) - its miroshark_simulate / miroshark_status tools wrap the full create → prepare → start → poll flow, so any Claude / Cursor / Windsurf session can launch a swarm. 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.

11x402

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 ingests your seed, extracts entities into a knowledge graph, spawns ~25 grounded agent personas, simulates 10 rounds of Twitter + Reddit + a Polymarket-style prediction market, and synthesizes a markdown report - belief drift, top posts, market trajectories, a knowledge-graph view.

Flat price, one dollar

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

No account, no key

There is no signup, OAuth, or API key. The only credential is a wallet that can sign a USDC transfer. After you pay, the returned run_id is the token for every follow-up call - polling and the report are public and unauthenticated.

Two chains, one endpoint

The same POST /run accepts USDC on either Base or Solana. The 402 challenge advertises both as separate accepts[] entries; your client pays on whichever chain its wallet is funded on.
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)
12Endpoint

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 or Solana - the 402 advertises both 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.

No gas needed

The facilitator sponsors the settlement fee on both chains - you fund the wallet with ~$1 USDC only, no ETH (Base) or SOL (Solana) required. On Base 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"

Always trust the authoritative values in the live 402 response's PAYMENT-REQUIRED header (base64 JSON, field accepts[]) over anything hardcoded - a real x402 client reads them for you at call time. The values above are just a head-start so you can pre-fund the right wallet.

13Protocol

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 CDP facilitator.

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

Every step above is automatic with an x402-aware client: it catches the 402, decodes accepts[], signs against the matching chain's requirements, 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"
  }
}
14Clients

Pay from any runtime

Drop-in code for the official x402 SDK in Python (Base + Solana) 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, settlement 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"

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, USDC 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.

15Inputs

Tuning the run & earning

One optional field rides along with any seed: prediction_market to pin the central market. Attach a Base Builder Code to your payment client to earn on the runs you drive - a share of each run from MiroShark (10% by default), plus Base's own builder rewards, both from the same on-chain attribution. 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 - it plans ~3 searches, runs them against a web-search model, and synthesizes 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
 }}

builder code - earn a share of every run you drive

Attach your own Base Builder Code to the x402 client you pay with, and every run you drive is attributed to your app on-chain (ERC-8021). That attribution earns you two ways on the same runs: MiroShark routes your share (10% by default) into a per-builder 0xSplits split at settlement, enforced on-chain - and it also counts toward Base's own builder rewards (base.dev analytics + program rewards).

You earn 10% of each run's $1 payment by default, paid on-chain into an immutable, ownerless 0xSplits contract. Nobody - MiroShark included - can redirect it or change the share. Anyone can trigger the payout, at any time, from the x402aff dashboard; the funds only ever reach the builder and the seller, so whoever clicks pays the gas and receives nothing extra.

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

Get a code at base.dev → Settings → Builder Codes (format ^[a-z0-9_]{1,32}$). Attribution is written on-chain on Base mainnet via the CDP facilitator; verify any settlement at buildercode-checker.vercel.app.

run the same thing on your own API

None of this is MiroShark-specific. The revenue share is an open-source kit, x402aff, and any x402 seller can pay their own builders the same way. It 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…" });
const payTo = await aff.payToFor(req.headers);   // the split, or your wallet
const extensions = aff.extensions;               // declares your code

Set your route's payTo to aff.payToand 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. The x402aff dashboard is this kit running live against MiroShark, and its All sellers view lists every split the kit has routed on Base, whoever runs it.

16Polling

Follow the run & read the report

The run_id is the token - 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. 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. One subtlety: 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.

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 leading with the outcome, agent/round counts and spend - 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.
17Free

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 you can drop straight into /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.
18Discovery

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”

Tags Simulation, Research, Search, Crypto, AI, with a service 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 first making a paid request.

Where MiroShark indexes

  • CDP Bazaar - indexes on each successful settle; reads the service metadata + bazaar input/output 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
19Reference

Wire-format reference

If you don't use a v2-aware SDK, here are the exact bytes: the base64 PAYMENT-REQUIRED challenge with both 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>"} }
  ]
}
Two entries - Base (EVM) and Solana - both $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 and send it in 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 feePayerto 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 - the value is a base58 signature). The same payment_chain / payment_network ride along in the 202 body and every /status response.

Full reference: the x402 v2 specification.

20Caveats

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 retryingso you don't double-pay.

No automatic refund on failure

If a run shows failed or budget_exceededafter 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 /runonly 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.

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.