Docs

How SI is built, the public API it exposes, and how to run it for free.

Architecture

One Next.js application. Data retrieval, rules, explanation and presentation are separate modules:

  • lib/providers: DEX Screener, Solana RPC and EVM RPC adapters (Robinhood Chain, Ethereum, Base, BNB), each with timeouts and typed errors.
  • lib/analysis/evm.ts: ABI decoding and bytecode inspection for owner, proxy, mint, pause, blacklist, fee and trading-switch functions.
  • lib/analysis/normalize.ts: turns raw provider payloads into facts. Pure and unit tested.
  • lib/analysis/rules.ts: deterministic rules with published thresholds. Pure and unit tested.
  • lib/analysis/verdict.ts: the built-in voice. llm.ts is an optional, guarded rewrite.
  • lib/analysis/scan.ts: orchestration, streaming progress, caching and in-flight de-duplication.
  • lib/arena: moderation, claim detection and checking.
  • lib/store: JSON file persistence and the in-process event bus behind the live feed.

Credentials never reach the browser. The browser only talks to this app’s own API routes.

Public API

Scan a token (streaming)

GET /api/scan?address=<token>&chain=robinhood|solana|ethereum|base|bnb[&force=1]
Content-Type: application/x-ndjson

{"type":"step","id":"market","status":"done","detail":"3 pair(s) indexed","ms":412}
...
{"type":"report","cached":false,"report":{ ... }}

Which chain is this 0x address on?

GET /api/detect?address=0x…  →  { chain: "base" | null, reason: "pairs" | "code" | "known" | "ambiguous" | "none" | "unreachable", options: [...], candidates: [...] }

Asks DEX Screener for indexed pairs and each EVM chain’s RPC for contract code, in parallel. Pairs win (highest liquidity), then code on exactly one chain. Code on several chains is ambiguous; nothing found is none; no source answering is unreachable. In those cases chain is null and the visitor picks: SI never falls back to a default chain. /api/scan and the Arena run the same lookup when a 0x address arrives without a chain, and refuse (422) rather than guess.

Latest report

GET /api/report/<token>?chain=<chain>  →  { report, history, scanCount, rank }

SI Score and the ranked board

report.score  →  { points: 0..100, tier: "fortified" | "solid" | "watchlist" | "fragile" | "hazard", confidence, checks, deductions: [...], cappedBy }
GET /api/rank?chain=<chain>&limit=50  →  { rows: [{ address, chain, symbol, points, tier, liquidityUsd, scannedAt, ... }], total, mode }

The SI Score starts at 100 and loses points for every finding by severity (critical 55, high 22, medium 9, low 4, no data 2) and 7 for each core check without data. It can never say more than the posture label: a critical finding caps it at 24, high at 44, elevated at 64, moderate at 84; fewer than half the checks with data caps it at 59. Tiers: 85+ FORTIFIED, 65+ SOLID, 45+ WATCHLIST, 25+ FRAGILE, below HAZARD. It measures what SI could see. It is not a return prediction and never a buy or sell call. The board ranks real scans only.

Arena

GET  /api/arena?limit=20&before=<iso>
POST /api/arena  { "text": "ownership is renounced", "address": "0x…", "chain": "robinhood" }
GET  /api/waitlist               { count }
POST /api/waitlist               { "handle": "@you", "wallet": "0x…" }
GET  /api/arena/<id>

Live feed

GET /api/live?limit=50&type=signal      (JSON)
GET /api/live/stream                    (Server-Sent Events, event: feed)

Share cards

GET /api/card/scan/<chain>/<token>    1200×630 PNG
GET /api/card/arena/<id>     1200×630 PNG

Operator endpoints

GET  /api/health
GET  /api/cron/observe            Authorization: Bearer $CRON_SECRET
GET  /api/admin/moderate          Authorization: Bearer $ADMIN_TOKEN
POST /api/admin/moderate          { "id": "...", "status": "hidden" }
POST /api/admin/corrections       { "subject": "...", "note": "..." }
GET  /api/admin/waitlist?format=csv

Rate limits apply per IP: scans and challenges are throttled to protect the free upstream APIs.

Configuration

VariablePurposeDefault
NEXT_PUBLIC_SITE_URLPublic URL, used for share links and cardshttp://localhost:3000
SOLANA_RPC_URLYour own Solana RPC endpoint (optional)public mainnet RPC
ROBINHOOD_RPC_URLYour own Robinhood Chain RPC (optional)rpc.mainnet.chain.robinhood.com
ETHEREUM_RPC_URLYour own Ethereum RPC (optional)ethereum-rpc.publicnode.com
BLOCKSCOUT_URL_ROBINHOOD / _ETHEREUM / _BASE / _BNBKeyless Blockscout explorer per chain: largest holders and holder countsRobinhood, Ethereum and Base instances; none for BNB
ETHERSCAN_API_KEYEtherscan API V2 key, server-only: verified source, deployer, deployment time, recent transfersnone (explorer checks off)
ETHERSCAN_CHAINSChains to ask Etherscan about (Robinhood Chain is robin.etherscan.io). A chain the plan does not cover is skipped after one refusalethereum,robinhood,base
BASE_RPC_URLYour own Base RPC (optional)mainnet.base.org
BNB_RPC_URLYour own BNB Chain RPC (optional)bsc-dataseed.binance.org
HELIUS_API_KEYFree Helius key: faster RPC and token metadata (optional)none
LLM_API_URL / LLM_MODEL / LLM_API_KEYAny OpenAI-compatible endpoint, e.g. local Ollama (optional)built-in rules engine
SCAN_CACHE_SECONDSReuse a scan for this long60
DATA_DIRWhere the JSON store lives. Keep it outside the app and deploy folders~/agent-si-data
WATCHER_INTERVAL_MINUTESScan newly listed tokens on a timer (0 = off)0
CRON_SECRETProtects /api/cron/observenone (endpoint disabled)
NEXT_PUBLIC_X_HANDLE, NEXT_PUBLIC_TELEGRAM_URL, NEXT_PUBLIC_DISCORD_URL, NEXT_PUBLIC_GITHUB_URL, NEXT_PUBLIC_DEXSCREENER_URL, NEXT_PUBLIC_CONTACT_EMAILThe social bar. Unset channels show as "soon"none
ADMIN_TOKENProtects moderation and corrections endpointsnone (endpoints disabled)
SI_DEMOServe labelled fixtures instead of live datafalse
NEXT_PUBLIC_TOKEN_ADDRESSOfficial contract once deployedempty = not deployed
NEXT_PUBLIC_TOKEN_VENUE_URL / _NAMEOfficial trading venue; the Buy button appears only when setnone (no Buy button)
NEXT_PUBLIC_X_HANDLEX account for Follow buttons and post tagsnone
NEXT_PUBLIC_HELMET_GLB_URLProduction 3D helmet (e.g. /models/agent-si-helmet.glb); HIGH tier only, falls back to the 2.5D helmetnone (2.5D helmet)

Run it

npm install
npm run build
npm start            # http://localhost:3000

On a VPS, run one instance with PM2 in fork mode so the live feed and store share a process, and put Nginx in front with proxy_buffering off for /api/live/stream and /api/scan. The README in the repository has the full config.