Skip to content

Documentation

Setup, configuration and capabilities.

Everything an operator needs to run RagnaLens and everything a reader needs to interpret a report. The plain-language version lives on the methodology page.

Local setup

npm install
cp .env.example .env.local   # optional: dedicated provider URLs, site URL, social links
npm run dev                  # http://localhost:12700
npm run test                 # deterministic evaluation, replay, stale-request tests
npm run typecheck && npm run build

Environment variables

VariablePurpose
RAGNALENS_RPC_URL_MAINNETServer-only. Dedicated JSON-RPC endpoint for chain 4663 (for example an Alchemy URL). Defaults to the documented public endpoint. Never sent to the browser.
RAGNALENS_RPC_URL_TESTNETServer-only. Same for chain 46630.
RAGNALENS_RATE_LIMIT_PER_MINUTEScans per minute per client IP (default 12). In-memory; put a real limiter in front for production.
NEXT_PUBLIC_DEFAULT_CHAIN_IDDefault network in selectors (default 4663). Must be enabled.
NEXT_PUBLIC_ENABLED_CHAIN_IDSComma-separated list of enabled networks (default: both). Disabled networks show “Network disabled”.
NEXT_PUBLIC_SITE_URLCanonical site URL for metadata and share links.
NEXT_PUBLIC_X_URL, NEXT_PUBLIC_GITHUB_URLOptional social links. Omitted links are not rendered.
NEXT_PUBLIC_TOKEN_ADDRESS, NEXT_PUBLIC_TOKEN_DEX_URL$RAGNA contract address (blank = “CA: Soon”) and optional trading link. Publish with npm run token:ca -- 0x…, which verifies bytecode and ERC-20 metadata on chain 4663 before updating Vercel and redeploying.
RAGNALENS_REFERENCE_TARGET, RAGNALENS_REFERENCE_CHAIN_ID, RAGNALENS_REFERENCE_LABELServer-only. The contract that /api/reference scans (cached ten minutes) to populate the landing page with a real report. Default: the wrapped-native token on chain 4663.

Verified network configuration

NetworkChain idCurrencyExplorerSourcify
Robinhood Chain4663ETHrobinhoodchain.blockscout.comsupported
Robinhood Chain Testnet46630ETHexplorer.testnet.chain.robinhood.comsupported

Sources checked on 2026-09-15: Robinhood Chain developer docs — Connecting; viem chain definitions (robinhood, robinhoodTestnet). Public RPC endpoints: https://rpc.mainnet.chain.robinhood.com and https://rpc.testnet.chain.robinhood.com. Robinhood Chain is an Arbitrum-stack Layer 2 on Ethereum; ETH is the gas token.

Landing page data

Nothing on the landing page is sample data. GET /api/reference runs a real scan of the reference contract through the same pipeline as the inspector, reuses it for ten minutes, and returns the last successful report marked stale if the provider fails afterwards. The hero labels, the walkthrough card and the evidence desk read from it and say “unavailable” when it is. The machine geometry is a visual metaphor and is labelled as such.

Provider capabilities

  • Reads go through a server-side relay that only forwards an allow-list of read methods. Hosts under robinhood.com are resolved via DNS-over-HTTPS because some ISPs hijack them; TLS still validates against the real hostname.
  • The public endpoints throttle bursts. The relay retries 429s three times with backoff, then reports rate limiting honestly.
  • The public nodes accept EIP-1898 block-hash objects with requireCanonical (verified 2026-09-15), so scans pin by hash when the probe succeeds and fall back to number-pinning with an end-of-scan hash check.
  • Each scan verifies that the endpoint reports the configured chain id before reading anything.

Supported observations

ObservationDescriptionStatus
derived.address.targetAddress syntax validation and checksummed form.Implemented
rpc.eth_getCode.targetRuntime bytecode at the recorded block, with size and keccak256.Implemented
rpc.eth_getBalance.targetNative balance at the recorded block (ETH, 18 decimals).Implemented
rpc.eth_call.name / symbol / decimals / totalSupplyToken metadata calls; bytes32-style names are decoded too.Implemented
rpc.eth_call.supportsInterface.*ERC-165 probes for ERC-165, ERC-721 and ERC-1155.Implemented
rpc.eth_getStorageAt.*EIP-1967 implementation/beacon/admin, EIP-1822 proxiable and legacy OpenZeppelin slots.Implemented
rpc.eth_call.beacon.implementationimplementation() on a beacon when the beacon slot is set.Implemented
rpc.eth_getCode.implementationBytecode at the resolved implementation.Implemented
derived.eip1167.targetEIP-1167 minimal proxy pattern match.Implemented
rpc.eth_call.owner / getOwner / pausedOwnership and pause getters.Implemented
derived.selectors.effectivePUSH4 selector scan over the effective bytecode (implementation for proxies).Implemented
explorer.sourcify.targetSourcify v2 verification lookup (unpinned; own retrieval time).Implemented
Blockscout verification / holders / transfersBlockscout answers a browser challenge to server-side callers on these networks.Unsupported
Holder distribution, liquidity, transaction history, auditsNo supported data source is wired in; never inferred.Unsupported

Rule definitions (v1.0.0)

IdTitleRequires
RL-001Address syntax
The target is a syntactically valid 20-byte EVM address. It is normalized and, when mixed-case, its checksum must match.
derived.address.target
RL-002Runtime bytecode
Reads the code at the target for the recorded block. No code means the address is not a contract at that block; it says nothing about intent.
rpc.eth_getCode.target
RL-003Native balance
Records the native currency balance at the recorded block. Informational only.
rpc.eth_getBalance.target
RL-004Token interface
Attempts name, symbol, decimals and totalSupply, plus ERC-165 interface declarations. A responding call is evidence of an interface, not proof of standard compliance.
rpc.eth_call.name, rpc.eth_call.symbol, rpc.eth_call.decimals, rpc.eth_call.totalSupply
RL-005Proxy pattern
Reads the standardized EIP-1967, EIP-1822 and legacy OpenZeppelin proxy slots and checks for the EIP-1167 minimal proxy runtime. Unrecognized patterns are not ruled out.
rpc.eth_getStorageAt.eip1967.implementation, rpc.eth_getStorageAt.eip1967.beacon, rpc.eth_getStorageAt.eip1967.admin, rpc.eth_getStorageAt.eip1822.proxiable, rpc.eth_getStorageAt.zeppelinos.implementation, derived.eip1167.target
RL-006Ownership getter
Attempts owner() and getOwner(). A set owner is a privileged party; a zero owner does not prove every privileged role is gone; a failed call proves nothing about administration.
rpc.eth_call.owner, rpc.eth_call.getOwner
RL-007Pause capability
Reads paused() and looks for pause()/unpause() selectors. A pausable contract can have its transfers halted by a privileged role.
rpc.eth_call.paused, derived.selectors.effective
RL-008Privileged selectors
Scans the runtime bytecode (and the resolved implementation, for proxies) for known privileged function selectors such as mint, blacklist and upgradeTo. Heuristic: selectors can be missed or misread.
derived.selectors.effective
RL-009Source verification
Asks Sourcify whether verified source exists for the target. The answer is recorded with its own retrieval time and is not pinned to the block.
explorer.sourcify.target

Inspected proxy slots: eip1967.implementation, eip1967.beacon, eip1967.admin, eip1822.proxiable, zeppelinos.implementation. Tracked selectors: 23 (15 privileged).

Deterministic replay

The evaluator is a pure function of (chainId, target, block, rulesetVersion, observations). Observations are normalized (hex lower-cased, sorted by id, duplicates rejected) before evaluation and hashing, so read order cannot change the result. The evidence id is sha256 over the canonical JSON (sorted keys, compact) of that input. Import a report on the inspector to validate it, recompute the id and re-run the rules locally.

Known limitations

  • Blockscout for these networks challenges server-side callers, so explorer verification comes from Sourcify only. “Verification status unavailable” is shown when Sourcify cannot be reached.
  • The rate limiter is per process. On serverless platforms each instance has its own bucket.
  • Reports are cached in memory per exact (chain, target, block, rule version) only when every read completed; a new scan always pins a new block.
  • Share links start a new scan. There is no server-side snapshot store; use the JSON export to keep a specific block’s report.
  • Selector scanning and proxy slot inspection are heuristics with documented false negatives.

Deployment

npm run build && npm run start          # Node server on :12700
# or on Vercel: import the repo, set the RAGNALENS_* / NEXT_PUBLIC_* variables, deploy.
# API routes run on the Node runtime; /api/inspect streams NDJSON and allows up to 60 s.

Security notes

  • All request inputs are validated server-side with Zod; addresses are re-parsed and checksummed.
  • Only the two allow-listed endpoints are ever contacted; user input can never choose an RPC URL.
  • No wallet connection, signatures, approvals or transactions exist anywhere in the product.
  • Token metadata and explorer content are sanitized, length-bounded and rendered as plain text only.
  • Imported reports are schema-validated and size-bounded before anything is rendered.