Docs
Signalith is a per-request market for signed observations. A client pays in USDG over x402, an open payment protocol layered on HTTP status 402, and receives a report signed under EIP-712. Any contract can verify that report against the on-chain signer registry, so a paid answer does not have to be a trusted one.
The packages on this page are not on npm yet
/v1, and works with curl or any x402 v2 client, as the first section shows. The MCP server is the exception: this site serves it, and the MCP page sets it up.Protocol overview
One request carries three separate mechanisms. They fail independently, and the sections below document each one on its own.
- Quote. An unpaid request for a paid resource answers
402with machine-readable payment requirements. - Settlement. The client signs an EIP-3009 transfer authorization. Signalith broadcasts it, so the client spends no gas and holds no native token.
- Verification. The response body carries an EIP-712 signed report. Verification is independent of payment and of this gateway.
The parameters this deployment enforces, read from the gateway rather than written into this page:
| Parameter | Value |
|---|---|
| Chain | Robinhood Chain, chain id 4663, CAIP-2 eip155:4663 |
| Payment asset | USDG, 6 decimals, at 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 |
| Payment protocol | x402 v2, scheme exact, settled by EIP-3009 transferWithAuthorization |
| Asset signing domain | EIP-712 {name: "Global Dollar", version: "1"}, the domain a payment authorization is signed under |
| Registry | 0x1Fd5CDDBCA88Ccf92507A0E21cd487D9842a1F7c |
| Verifier | 0x684482efbC4F169679b067f6A58700Af453D08fa |
| Platform fee | 10% for a provider registering now, taken at withdrawal and locked per provider at registration |
| Settlement floor | $0.20 per call, derived from a measured settlement cost of $0.005322 divided by the fee rate. Below it a feed sells packs, never single calls |
| Pack validity | 30 days from purchase, non-refundable |
| Gateway base URL | https://signalith-gw.187-53-130-188.sslip.io/gw, all paths under /v1 |
Two conventions hold across the whole API. Any integer that can exceed 253 travels as a decimal string in JSON, which covers values, amounts, sequences and millisecond timestamps. Amounts are atomic USDG at 6 decimals, so "2000" is $0.002.
Buy a report over HTTP
A feed is addressed either as {handle}/{slug} or as its 32 byte feedId. Both resolve to the same record, and both are accepted wherever a path below says {feed}.
- Request the resource with no payment. The answer is
402 Payment Requiredwith aPAYMENT-REQUIREDheader holding base64 JSON:accepts[], each entry carrying scheme, network, amount, asset,payToandmaxTimeoutSeconds. - Select an offer and sign the EIP-3009 authorization it describes, a transfer of
amounttopayTounder the USDG domain. Signing is off chain and costs nothing. - Repeat the request with the payload in
PAYMENT-SIGNATURE. The gateway verifies the authorization, settles it on chain, and returns200with the report envelope, the transaction hash inPAYMENT-RESPONSE.
curl -i https://signalith-gw.187-53-130-188.sslip.io/gw/v1/feeds/lumoracle/eur-usd-ecb-reference/latest # HTTP/1.1 402 Payment Required # PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6Mi… curl https://signalith-gw.187-53-130-188.sslip.io/gw/v1/feeds/lumoracle/eur-usd-ecb-reference/latest \ -H "PAYMENT-SIGNATURE: <base64 payment payload>" # HTTP/1.1 200 OK # PAYMENT-RESPONSE: eyJzdWNjZXNzIjp0cnVlLC…
The headers that carry the exchange. Every request header below is on the CORS allow list and every response header on the expose list, so a browser can both send and read them.
| Header | Direction | Carries |
|---|---|---|
| PAYMENT-REQUIRED | Response, 402 | Base64 JSON payment requirements, x402 v2 |
| PAYMENT-SIGNATURE | Request | Base64 JSON of the signed EIP-3009 authorization |
| PAYMENT-RESPONSE | Response, 200 | Base64 JSON with transaction, network and payer. Present only when this response settled a payment |
| X-Signalith-Pack-Calls-Left | Response, 200 | Calls remaining in the pack this call was spent from |
| Authorization | Request | Bearer with a session token or an API key |
| SIGN-IN-WITH-X | Request | Base64 JSON of a CAIP-122 message and its signature |
The SDK performs all three steps inside a budget fixed at construction:
import {Signalith} from '@signalith/sdk'
const signalith = new Signalith({
gatewayUrl: 'https://signalith-gw.187-53-130-188.sslip.io/gw',
account, // any viem account holding USDG
maxSpendUsdg: 5_000_000n, // 5 USDG, atomic, for the life of this instance
rpcUrl, // optional: resolve payTo and the signer on chain
})
const report = await signalith.read('lumoracle/eur-usd-ecb-reference')
report.value // bigint, scaled by report.decimals
report.text() // exact decimal text, no float in the path
report.encoded // pass with report.signature into a contract call| Option | Type | Effect |
|---|---|---|
| gatewayUrl | string | Required. Origin the client talks to |
| account | LocalAccount | Signs payments and sign-ins. Without it the client can search, preview and verify, but not buy |
| apiKey | string | A dashboard key (lk_…). Spends the packs of the wallet that made it and signs nothing. Cannot buy |
| network | eip155:${number} | The only network a payment may be on. Default: Robinhood Chain |
| asset | Hex | The only token a payment may be in. Default: the USDG pinned in the package, never the address the gateway offers |
| maxSpendUsdg | bigint | Hard budget in atomic USDG for the life of the instance. Default 5 USDG |
| maxPaymentUsdg | bigint | The most one payment may settle. A pack is one payment, so larger packs are never chosen. Default: no cap beyond the budget |
| allowedProviders | string[] | Provider handles the client may read from, compared without case. Default: any |
| allowedCategories | string[] | Category ids the client may read from. Default: any |
| allowedSignerKinds | ('managed' | 'provider' | 'aggregate')[] | Signer kinds the client accepts, on the feed and on each report. Default: any |
| minSourceCount | number | The fewest upstream sources a report may rest on, on the feed and on each report. Default: none |
| packPreference | PackPreference | What to buy when payment is needed. Default: a single call where the feed sells one, else the smallest pack |
| rpcUrl, registry | string, Hex | Resolve the vault and the signer set on chain instead of accepting the gateway answer |
Before it signs anything, the SDK rejects:
- an offer on any network but the configured one,
- any asset but the USDG pinned in the package,
- an amount that is not
pricePerCall × calls, - any payment that would take the instance past
maxSpendUsdg, - a payment above
maxPaymentUsdg, when it is set, - with
rpcUrlset, apayTothat is not the CREATE2 vault the registry derives for that feed’s provider.
The first five cost nothing and run offline. The sixth is one RPC call and is the one worth carrying into your own client: a vault address is a pure function of the provider id and the factory, so any other recipient is an answer Signalith did not produce. A read also signs in first, one free signature, so a pack bought by an earlier process is spent before anything is bought again.
The read limits, allowedProviders, allowedCategories, allowedSignerKinds and minSourceCount, are checked against the feed before any call is spent, pack calls included. The signer kind and the source count are checked again on the report, since a feed of three sources can sign a report that rests on two. A refusal is a POLICY error. charged is true on an error only when a report was paid for and then refused by these checks.
A stock x402 client works as well. Its spend controls only carry the assets it ships with, so USDG has to be allowed explicitly or every offer is rejected before it is shown:
spendControls: {
allowedAssets: [{
network: 'eip155:4663',
asset: '0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168',
maxAmountPerPayment: '2000000',
}],
}If you settle into Signalith vaults with your own facilitator, note that USDG does not revert on a replayed EIP-3009 nonce. It succeeds, moves nothing and emits AuthorizationAlreadyUsed. Read authorizationState(from, nonce) before sending, and confirm by the Transfer log rather than by receipt status alone.
Endpoint reference
Metadata, discovery and verification are free and need no wallet:
| Method and path | Returns |
|---|---|
| GET /v1/config | Network, USDG address and domain, contract addresses, fee for new providers, configured floor, live settlement gas estimate, pack validity and tiers |
| GET /v1/feeds | Feed summaries. Filters: q, category, provider, kind, maxPriceUsdg, sort. include=preview adds the delayed public value |
| GET /v1/feeds/{feed} | Full metadata: schema, methodology, sources with their URLs, rights declaration, heartbeat, pricing, signers, reliability |
| GET /v1/feeds/{feed}/series | Delayed unsigned series for charting, at most 160 points. range=24h|7d|30d |
| GET /v1/feeds/{feed}/reliability | Seven days of heartbeat bars, each ok, late or miss, measured by Signalith from stored reports |
| GET /v1/feeds/{feed}/preview | The value as of preview_delay ago, unsigned, with its observedAtMs. Rate limited, for display only |
| GET /v1/providers | Every provider with feed count, paid calls in 30 days and measured uptime |
| GET /v1/providers/{provider} | Profile, feeds, vault address, fee rate, pending payout change |
| GET /v1/stats | Feeds selling, providers, paid calls in 30 days, and the date counting started |
| GET /v1/activity | Recent on-chain settlements, newest first. Pack redemptions are off chain and are counted, never listed |
| POST /v1/verify | Body: a report envelope. Returns each check as pass or fail, the same logic as the SDK and SignalithVerifier |
| GET /v1/sessions/challenge | A fresh Sign-In-With-X challenge: domain, URI, nonce, issued-at, expiry, and the chains it may be signed for |
| POST /v1/sessions | Body {payload}. Exchanges a signed SIWx payload for a 15 minute session token, with the payer it resolved |
| GET /v1/packs | The caller packs. Needs a session or an API key |
| GET /.well-known/x402 | Discovery document: every paid resource with its offers |
| GET /llms.txt | Plain-text guide for agents: what is sold, how to pay, how to verify |
| GET /v1/health | Component status: gateway, database, chain lag, worker heartbeat, relayer balance |
Two endpoints are paid, and both answer in signed report envelopes:
| Method and path | Cost | Returns |
|---|---|---|
| GET /v1/feeds/{feed}/latest | 1 call | The latest report envelope for the feed |
| GET /v1/feeds/{feed}/history | A page price in calls, from a pack only | Up to 500 envelopes between two Unix millisecond times (from, to, limit), oldest first, inside the 48 hour retention. An empty range costs nothing. A page is worth a few calls, under the settlement floor for almost every feed, so it is never settled on its own: without a pack the answer is a 402 offering packs |
A paid endpoint accepts credentials in this order, and stops at the first one present:
Authorization: Bearerwith a session token fromPOST /v1/sessions,Authorization: Bearerwith a dashboard API key,SIGN-IN-WITH-Xwith a CAIP-122 payload,PAYMENT-SIGNATUREwith an x402 v2 payload answering a previous 402.
With none of them, or with a pack that has no calls left, the answer is 402.
Verify in a contract
Pass encoded and signature into your function and let SignalithConsumer apply the policy. Verification reads the registry and nothing else: it does not call the gateway, and it holds for a report bought by anyone, at any time inside its age bound.
import {SignalithConsumer} from "@signalith/contracts/src/SignalithConsumer.sol";
contract CardVault is SignalithConsumer {
bytes32 constant CARD_FEED = 0x…;
constructor(ISignalithVerifier v) SignalithConsumer(v) {}
function settle(bytes calldata report, bytes calldata sig) external {
int256 cents = _readOnce(CARD_FEED, report, sig, Policy({
maxAgeSec: 3_600,
allowedKinds: KIND_PROVIDER, // first party only
minValue: 1,
maxValue: 10_000_000
}));
}
}| Policy field | Type | Meaning |
|---|---|---|
| maxAgeSec | uint32 | Maximum age measured from observedAtMs. Zero reverts, so there is no way to read without naming a bound |
| allowedKinds | uint8 | Bitfield. KIND_MANAGED 0x02, KIND_PROVIDER 0x04, KIND_AGGREGATE 0x08, KIND_ANY their union |
| minValue, maxValue | int256 | Inclusive bounds on the reported value, in the feed scale |
Three entry points, differing only in what they record:
| Function | Records | Use for |
|---|---|---|
| _peek | Nothing | A view, or a check whose result you discard |
| _read | The last observation accepted, allowing the same one again | A contract that only keeps a current value |
| _readOnce | The last observation accepted, requiring a strictly newer one | A function that pays out, so one purchased report drives it once |
The verifier reverts unless every one of these holds, and each has its own error selector:
- the encoded report is exactly
ENCODED_REPORT_LENGTHbytes, so one signed report has one encoding, - the signature is 65 bytes with a low s value and recovers to a non-zero address,
- that address is a signer registered for this
feedId, and its registered kind equalsreport.signerKind, report.decimalsequals the decimals the registry holds for the feed,observedAtMsis withinmaxAgeSecof the block time and no more than 5 seconds ahead of it.
SignalithConsumer adds the feed identity check, the kind filter, the value bounds and the ordering rule on top. The same verification is one call from a terminal:
cast call 0x684482efbC4F169679b067f6A58700Af453D08fa \ "verifyEncoded(bytes,bytes,uint32)" <encoded> <signature> 3600 \ --rpc-url <rpc>
Agent integration
Signalith hosts the MCP server at https://signalith-gw.187-53-130-188.sslip.io/gw/mcp over Streamable HTTP, so an agent needs nothing installed: the free tools work with no key, and read_feed spends packs with an API key, sent as Authorization: Bearer lk_…. Claude's and ChatGPT's connectors sign in with OAuth instead: Connect opens a Signalith page to sign in with a wallet or paste a key. The hosted server holds no wallet and never pays. The MCP page writes the setup for Claude, ChatGPT, Claude Code, Cursor and VS Code.
An agent that pays from a wallet of its own runs the same server locally over stdio, with the wallet and the budget in its own process, so nothing the agent reads can raise the budget. Until the package is on npm, it is installed from the tarball this site serves, as below.
{
"mcpServers": {
"signalith": {
"command": "npx",
"args": ["-y", "https://signalith.xyz/downloads/signalith-mcp-0.1.0-3e833541c8.tgz"],
"env": {
"SIGNALITH_GATEWAY_URL": "https://signalith-gw.187-53-130-188.sslip.io/gw",
"SIGNALITH_PRIVATE_KEY": "<a wallet made for this agent>",
"SIGNALITH_MAX_SPEND_USDG": "1000000"
}
}
}
}| Tool | Pays | Does |
|---|---|---|
| search_feeds | No | Search and filter the catalog |
| get_feed | No | Metadata, schema, pricing, reliability |
| read_feed | Yes, inside the budget | Buys or spends a pack, returns value, payload and the verification result |
| verify_report | No | Checks a report envelope without buying anything, and with feed, that it is of that feed |
| Variable | Required | Effect |
|---|---|---|
| SIGNALITH_GATEWAY_URL | Required | Gateway origin. The server refuses to start without it |
| SIGNALITH_PRIVATE_KEY | Optional | The wallet that pays. Without it the server searches, describes and verifies, and refuses to buy. Give the agent a wallet of its own, funded with what you accept losing |
| SIGNALITH_API_KEY | Optional | Spends packs bought ahead and can never pay, whatever the agent is told. The tighter grant of the two |
| SIGNALITH_MAX_SPEND_USDG | Optional | Atomic USDG ceiling for the process lifetime. Default 1 USDG |
| SIGNALITH_MAX_PAYMENT_USDG | Optional | The most one payment may settle, a pack included |
| SIGNALITH_PROVIDERS, SIGNALITH_CATEGORIES | Optional | Provider handles and category ids the agent may read, separated by commas. Search results leave out the rest |
| SIGNALITH_SIGNER_KINDS | Optional | managed, provider, aggregate, separated by commas |
| SIGNALITH_MIN_SOURCES | Optional | The fewest upstream sources a report may rest on |
| SIGNALITH_RPC_URL | Optional | Where the server reads the chain to check payment recipients and report signers against the registry. Default: a public Robinhood Chain endpoint. off turns the checks off and trusts the gateway’s lists |
| SIGNALITH_REGISTRY | Optional | The registry to check against. Pinned for Robinhood Chain, never taken from the gateway |
| SIGNALITH_NETWORK, SIGNALITH_ASSET | Optional | Override the pinned network and asset, for a local development chain only |
The budget counts a payment from the moment it is signed, so several read_feed calls sent at once cannot pass it together. Every failure tells the agent whether it was charged: no, yes, or maybe when a payment was handed over and its settlement is uncertain. A payment that lands after all is credited to the wallet as calls on that feed, and the next read spends those first.
An agent framework that already speaks x402 needs none of this. Every offer carries Bazaar metadata with input and output schemas, and /llms.txt and /.well-known/x402 are the discovery surface.
Publish a feed
Open the dashboard and register as a provider, one transaction, then create a feed, one transaction each. Registration derives your vault address deterministically with CREATE2 and locks your fee rate at the value in force that day. The vault contract itself is deployed on first use.
| Signing mode | signerKind | Mechanism |
|---|---|---|
| Signalith, from your API | 1 | You supply an HTTPS endpoint and a JSON path. The worker fetches on your heartbeat and signs what it read, as a labelled notary. Every surface that shows the report carries that label |
| You, with your own key | 2 | You run the signer SDK on your own infrastructure. Reports are first party. The worker polls your signer once per heartbeat and re-checks every report against the registry before selling it |
| Aggregate | 3 | Cross-provider median with quorum and outlier exclusion. Specified, not built. See the roadmap |
import {createSigner} from '@signalith/signer'
export const handler = createSigner({
feedId: '0x…', // from the wizard
decimals: 2,
requester: '0x…', // Signalith's requester key, from the wizard
privateKey: process.env.SIGNALITH_SIGNER_KEY,
async observe() {
const sale = await db.medianSale('psa9-charizard')
return {value: BigInt(sale.cents), observedAtMs: sale.at, payload: {sales: sale.count}}
},
})The handler is a standard fetch handler, so it runs on Node 18 and later, Vercel, Cloudflare Workers, Deno and Bun. It serves GET /signalith/latest and GET /signalith/health, and refuses any request without a valid X-Signalith-Request header signed by requester. Set that field: without it the endpoint answers whoever finds it, and what it hands out is the signed report you sell. The sequence number is the observation time in milliseconds, so the handler holds no state. When observe() throws, no report is stored and the feed stops selling until it recovers, rather than selling a value that is past its heartbeat.
A provider cannot register its own key under kind 1 or kind 3. The registry accepts those only with an on-chain attestation from the Signalith attester key, so the label on a report is a claim the chain backs rather than one the provider made.
Payments move from the buyer’s wallet into your vault in a single transfer. Signalith never holds them, and no operator key can move a vault balance anywhere except to your payout address and the fixed fee share. The platform fee is 10% of a withdrawal, at the rate locked when you registered, and it goes to Signalith’s treasury.
Report format
A report is eleven fields signed under the EIP-712 domain {name: "Lumoracle", version: "1"}. The domain carries no chainId and no verifyingContract, deliberately: a report states a fact about the world, so the same signature verifies on any chain that mirrors the signer registry. Verify with the domain exactly as written; a report checked under any other name will not recover its signer.
| Field | Solidity type | Meaning |
|---|---|---|
| feedId | bytes32 | The feed this observation belongs to |
| sequence | uint64 | Ordering within the feed. For first party signers this is the observation time in milliseconds |
| observedAtMs | uint64 | When the value was true at the source. Every freshness rule is measured from here, never from publication |
| publishedAtMs | uint64 | When Signalith stored it. The gap between the two is what the reliability bars measure |
| value | int256 | The observation, scaled. Signed, so a negative rate or spread needs no offset |
| decimals | uint8 | The scale. Checked against the registry, so a feed cannot silently change it |
| confidence | uint256 | Spread across sources, in the same scale as value. Zero where a single source is definitive |
| payloadHash | bytes32 | keccak256 of the canonical JSON (RFC 8785) of everything that is not the primary number. Zero where there is no payload |
| requestHash | bytes32 | Binds request parameters into the signature. Zero until parameterized reads ship |
| sourceCount | uint16 | How many upstream sources the value was derived from |
| signerKind | uint8 | 1 managed, 2 provider, 3 aggregate. Must equal the kind the registry holds for the signer |
The envelope around it is what both the SDK and a contract consume:
{
"report": {
"feedId": "0x…", "sequence": "1790016052000",
"observedAtMs": "1790016052000", "publishedAtMs": "1790016052412",
"value": "10432", "decimals": 2, "confidence": "0",
"payloadHash": "0x…", "requestHash": "0x00…00",
"sourceCount": 3, "signerKind": 2
},
"signature": "0x…", // 65 bytes, EIP-712 over the fields above
"signer": "0x…", // recovers from the signature, checked against the registry
"payload": { "sales": 12 }, // canonical JSON of this hashes to payloadHash
"encoded": "0x…", // abi.encode of the report, what a contract takes
"feed": { "slug": "…", "provider": "…", "schema": "…" }
}Paste any envelope into the report checker to see each check pass or fail, or post it to POST /v1/verify for the same result as JSON.
Packs, sessions, keys
Settling a payment on chain costs gas, which Signalith pays out of the platform fee. The break-even is the settlement cost divided by the fee rate, which is where the floor of $0.20 per call comes from. The gateway raises it per provider when live gas divided by that provider fee rate is higher. A feed priced below the floor never settles a single call: it sells packs, one payment for N calls, spent afterwards with a wallet signature and no transaction.
A pack belongs to the wallet that paid for it, so spending one means proving that wallet. Packs last 30 days and are not refundable, because the money is already in the provider vault. A provider cannot archive a feed while unexpired packs exist against it.
In a browser, open a session:
curl https://signalith-gw.187-53-130-188.sslip.io/gw/v1/sessions/challenge
# {"info":{"domain":"…","uri":"…","version":"1","nonce":"…",
# "issuedAt":"…","expirationTime":"…","statement":"…"},
# "supportedChains":[{"chainId":"eip155:…","type":"eip191"}]}
curl -X POST https://signalith-gw.187-53-130-188.sslip.io/gw/v1/sessions \
-H "Content-Type: application/json" \
-d '{"payload": <the SIWx payload, object or base64 string>}'
# {"token":"…","payer":"0x…","expiresInSec":900}On a server, make an API key in the dashboard instead. Both spend the packs of the same wallet, and every answer carries how many calls are left.
curl -i https://signalith-gw.187-53-130-188.sslip.io/gw/v1/feeds/lumoracle/eur-usd-ecb-reference/latest \ -H "Authorization: Bearer lk_…" # HTTP/1.1 200 OK # X-Signalith-Pack-Calls-Left: 99
A key is lk_ followed by 43 characters, shown once, stored only as its SHA-256. It spends and cannot buy: it holds no wallet and signs no payment, so a leaked key costs at most the calls left and never moves USDG. When its wallet has no call left on a feed the answer is the ordinary 402 quote, and the next pack is bought with the wallet. A wallet holds up to 10 keys and can revoke any of them at any time. Keys are created and revoked only by the web app behind its own sign-in; the gateway exposes no route that creates one.
Error codes
Errors are {"error": {"code": "…", "message": "…", "retryAfterMs": 5000}}, with the code the stable part. There is no error for which a buyer is charged: settlement is attempted only after a sellable report exists, and the report is returned only after settlement confirms.
| Code | HTTP | Meaning |
|---|---|---|
| BAD_REQUEST | 400 | The request was not understood. |
| PAYMENT_INVALID | 401 | The payment authorization was not accepted. |
| SETTLEMENT_FAILED | 402 | The payment could not be settled on chain. |
| FEED_UNAVAILABLE | 403 | This feed is not selling right now. |
| FEED_NOT_FOUND | 404 | This feed does not exist. |
| RATE_LIMITED | 429 | Too many requests. Wait a moment and try again. |
| BAD_REPORT | 502 | The provider returned a report that failed its signature check. |
| STALE_DATA | 503 | The latest report is too old to sell. Try again when the feed recovers. |
| SERVICE_UNAVAILABLE | 503 | Signalith cannot take payments right now. |
Two further codes exist in the wire type and are never sent by the gateway. PACK_EMPTY is raised by the SDK before it calls, when the key you gave it has no pack left and no account to pay from. PROVIDER_TIMEOUT is reserved: a first party signer that stops answering leaves the last report standing, and the feed turns STALE_DATA once that report passes twice its heartbeat. Handle both, as any client should handle a code it does not know.
Roadmap
What is listed as built is in the code today. None of it runs on Robinhood Chain until Signalith’s contracts are deployed there. What is planned is specified and unscheduled, and none of it is a commitment with a date attached.
Built
- Contracts, deployed on 21 September 2026:
SignalithRegistry,SignalithVerifier, and oneSignalithVaultper provider at a CREATE2 address, deployed lazily on first use. - Test evidence: 95 Foundry tests at 100% line, branch and function coverage of the four contracts, six invariants at 256 runs and 16,384 calls each, and a fork test of real USDG settlement into a vault.
- x402 v2 gateway with its own facilitator in process, EIP-3009 settlement, packs, SIWx sessions and API keys.
- Managed connectors and first party signer intake, heartbeat polling, and reliability measured from stored reports rather than claimed.
- Consumer SDK, signer SDK, MCP server and Solidity consumer library, built and tested, publication pending.
- On-chain verification through
verifyEncoded, plus the same checks off chain at /verify andPOST /v1/verify.
In progress
- Deploying the contracts on Robinhood Chain and publishing their addresses.
- Publishing the four packages to npm under their final scope, which is what freezes the names in these snippets.
- Widening the catalog to feeds whose upstream terms permit resale, each with its terms quoted in the feed methodology.
- First party providers running their own signers, which is the mode the protocol is built around.
Planned
| Item | What it adds |
|---|---|
| Parameterized reads | GET /v1/feeds/{feed}/query, a provider-signed answer to a question with parameters, with requestHash bound into the signature. Needs a per-request round trip to the provider signer, which the current pull model does not make |
| Sub-floor per-call payment | x402 upto through Permit2, or a deferred voucher escrow where the buyer deposits once, signs cumulative vouchers and the provider redeems in batches. Also the answer to pack refunds |
| Aggregate feeds | Cross-provider median at signerKind 3, with quorum, outlier exclusion and published confidence |
| Provider reputation | A score derived from measured uptime, latency, deviation from aggregates and takedown history. Not a token |
| Verification fee | An optional per-feed fee charged by the verifier and paid to the vault, which is the answer to free riding on a resold report |
| Push adapter | A permissionless contract that stores the latest verified report and exposes AggregatorV3Interface, so consumers written for the Chainlink shape can read a Signalith feed unchanged |
| Streaming | SSE or WebSocket delivery for pack holders, instead of one HTTP round trip per read |
| Tooling | Python SDK, CSV export, provider alerts, a status page |
The contracts carry no third-party audit