← Hanko
Docs

How it actually works, in full.

The landing page shows what the agent does. This is the rest of it: where the layer boundaries sit, what the MCP transport actually handles, what a live x_search call really returns, how the exit-cost model is built, and what's proven against real, live calls to the platform.

Layers

Six packages, each owning one guarantee.

The codebase is organised around what each layer promises, not around what it happens to import. Read top to bottom, the guarantees compose: a decision cannot cite evidence that was not stored, and cannot be sized on a market fact that was fabricated.

PackageOwnsGuarantee
hanko/sources X via xAI x_search, RSS and Atom, fixtures, one adapter contract fetch() may fail; parse() is pure
hanko/snapshot Append-only, content-addressed store; replay; integrity checking Altered bytes cannot load silently
hanko/ryotools The six RYO tools over MCP and REST; structural fact extraction Read-only; no write path exists
hanko/decision Readings, independence, evidence quality, verdicts, pre-registration decide() is a pure function
hanko/review Falsifier checking, calibration, per-voice and per-rule track records One review per decision, enforced
hanko/skills Research skills contributed back to the tool surface, e.g. exit_liquidity Same read-only, honesty-first contract

The purity boundary on the homepage sits between hanko/decision and everything upstream of it: collection and interpretation may touch the network and a model; deciding and reviewing may not.

Transport

The MCP transport, in full.

The hackathon issues an MCP credential, so MCP, not REST, is the path known to exist. This is a small, dependency-free JSON-RPC 2.0 client over Streamable HTTP, covering exactly the handshake the six read-only tools need.

initialize → notifications/initialized → tools/list | tools/call

It accepts both plain JSON and server-sent-event responses, because assuming one and failing on a server that streams would be a guess about someone else's implementation. It honours the published 60-per-minute rate limit by spacing calls rather than discovering the limit through 429s. And it records which of the three possible result shapes each payload arrived in (structuredContent, JSON embedded in a text block, or plain text) rather than assuming one, because a server can change shape between versions and a snapshot should say which shape it was captured in.

The transport stops at the source boundary. A handshake failure, a rate limit, a protocol error, and an isError tool result all become a recorded snapshot with the reason attached, never an exception that escapes and turns a recordable fact into a lost one.

A test asserts that facts extracted from an MCP payload are byte-identical to facts extracted from a REST payload for the same data. Swapping transport touches no line of reasoning code: the six tools, the decision engine, and the review loop cannot tell which one supplied a number.

Track 1

The engine sizing exactly to the evidence, live.

Both ran against the real xAI and RYO APIs, not a fixture. Each is shown because it is proof the rules hold under real conditions, the same rules that size a confident ENTER when the evidence actually clears the bar.

The evidence gate refuses before it ever reasons

hanko decide x --token SOL --subject elonmusk, run live on 6 September 2026. The account's recent posts were fetched for real, and none of them mentioned SOL by its cashtag, so the decision stops at the first gate rather than guessing from a name that sounds related.

ABSTAIN SOL · dec_dc9abf2850fcd5a76df9ae34LIVE OUTPUT
ABSTAIN SOL  size 0.0%  confidence 0.0
  evidence quality 0.0  (completeness 0.0, freshness 0.0, corroboration 0.0, independence 0.0)
  x evidence_present: no readings reference SOL
  x quality_floor: evidence quality 0.0 is below the floor of 0.4
  x independent_voices: 0 independent voice(s) of 0 author(s); 0 echo(es) discounted; policy requires 2
  x direction: evidence does not argue for entry
  . exit_liquidity: no pool liquidity available; exit cost at size is unknown and is not assumed to be zero
  ? gap market_field_missing: liquidity_usd, price_usd, safety_score, volume_24h_usd
  review at 2026-09-09T20:23:33Z
Evidence found, and still not enough to act on

hanko sweep against a one-token watchlist (BTC, source CryptoKaleo), run live on 7 September 2026. This time a reading was produced, real RYO market facts came back too, and the decision still lands on PASS: one voice is not the two independence requires, and a neutral-leaning post does not argue for entry either way.

PASS BTC · dec_40ffc8b5e8c5a892ced98a82LIVE OUTPUT
PASS BTC  size 0.0%  confidence 0.0
  evidence quality 0.486  (completeness 0.5, freshness 0.23, corroboration 0.33, independence 1.0)
  + evidence_present: 1 reading(s) reference BTC
  + symmetry: evidence is directionally neutral (0 vs 0)
  + quality_floor: evidence quality 0.486 clears 0.4
  x independent_voices: 1 independent voice(s) of 1 author(s); 0 echo(es) discounted; policy requires 2
  x direction: evidence does not argue for entry
  . exit_liquidity: no pool liquidity available; exit cost at size is unknown and is not assumed to be zero
  ? gap market_field_missing: liquidity_usd, safety_score
  ! wrong if independent_voices > 1.0 within 72.0h (verdict flips if another independent voice converges)
  ! wrong if volume_24h_usd > 31998332596.12 within 72.0h (verdict is worth revisiting if volume rises 50%)
  review at 2026-09-10T04:30:04Z

This is the proof: run against real posts and real market facts, the engine sizes exactly to what the evidence supports, an ABSTAIN when nothing was said, a PASS when one voice wasn't enough. Same rules that would size a confident ENTER, holding under real conditions.

Track 3

exit_liquidity: can you actually get out?

Confirmed against the real, authenticated catalog: six tools, all answering some version of "is this worth entering?" analyze_token and deep_analysis say what a token is doing; there is no safety tool at all, only a qualitative risks list. None of the six answer whether a position can be closed, or what closing it costs, and a token can clear every measured signal and still be a position you cannot exit without giving back a tenth of it.

Modelled, not measured

No order book is observed anywhere in this skill, so the Confidence type has no HIGH value at all. The enum simply does not contain one. Every figure is computed from a constant-product curve (slippage = f / (1 + f) where f is the exit size as a fraction of pool depth) against reported liquidity, and the response carries the model id and its five stated assumptions alongside every number. Past 25% of the pool the model says so and downgrades its own confidence, rather than extrapolating a figure it does not believe.

A missing input silences the answer it feeds, and only that one. Absent liquidity returns null and a verdict of unknown, never zero, which would read as "free to exit." Time to exit, which takes size, volume and participation and never touches pool depth, is still answered. That split is the normal case against the live platform, which publishes volume for every token and depth for none: withholding the answer it can support would be over-refusal, and costs a caller a real number just as surely as inventing one misleads them.

UNKNOWN BONK · a $20m position, live RYO dataLIVE OUTPUT
UNKNOWN  BONK  confidence none
  price $0.00000337 · 24h volume $137,702,816  (analyze_token)
  exit over 34.9h at 10% of volume
  this market absorbs $573,762 per hour at that cap
  waiting that long is exposed to ~8.6% price drift at this market's volatility
  turnover 46.4% of market cap per day; this position is 6.732% of cap
  ? liquidity_usd unavailable; exit cost cannot be modelled and is
    reported as null rather than zero
  modelled with cpmm_v1, not observed

It is also the size gate inside the decision engine, importing the same model rather than a second copy of the arithmetic, so the number the agent sizes a position on and the number this tool publishes cannot drift apart. 29 tests, offline, no network. Run it yourself at /try.

Test coverage

What's actually proven.

Every line below maps to a real test or a real call against the live platform, not a claim taken on faith.

Proven · 351 tests, all offline, about 5 seconds

Every one of these lines maps to a real test. Read them, or run them yourself.

Read the code