Varnir
Scanner Playground Docs

Live on Ethereum Sepolia & Tron Nile testnets

A payment rail
AI agents can be
trusted with.

Software agents are starting to spend money. Varnir is the ledger underneath them: one agent proposes a payment, other agents verify it, and nothing moves until they have.

Testnet. The ledger, wallets, exchange, invoicing and SDK are built and running on public test networks, with no real funds. Two-key approval is built; the wider multi-verifier layer is where this is going, not something you can use today. This page says which is which.

01 — The problem

An agent that can pay is an agent that can be wrong expensively.

Give a model a key and it can move money. That is the whole security review. Nothing sits between the inference and the irreversible transaction: no second opinion, no policy check the model cannot talk its way past, no record of why the payment was thought correct.

  • i

    One model, one signature, no recourse

    One agent holding one private key is a single point of failure. Prompt injection, a hallucinated invoice, a poisoned tool response — each ends the same way: a valid signature over a payment nobody sanctioned.

  • ii

    Custody is the actual risk

    The usual fix is to hand the key to a platform. Now the platform can move your funds, and its breach is your breach. Swapping one unaccountable signer for another is not a control.

  • iii

    Nothing to audit afterwards

    "The agent decided to" is not an audit trail. Without a record of what was proposed, what checked it and why it passed, nobody can reason about an automated treasury — and nobody accountable will sign off on one.

02 — The approach

Put agents on both sides of the transaction.

An agent proposes a payment. It does not execute one. Independent verifying agents check the proposal against treasury policy, and only an approved proposal settles. The proposer never holds unilateral authority, because authority is the thing being split.

The rail — your policy plugs in Proposing agent "pay 4,200 USDT" Verifier counterparty + limits Verifier policy + budget Verifier anomaly + duplicate Settlement on-ledger, final
Varnir enforces the split — who may propose, who may confirm, and that no key confirms its own proposal. What each verifier checks is your policy to write, not ours to prescribe. Every decision, approval or refusal, is a record you can go back and read.

That is the shape it is heading for. The first rung is built and running on public testnet today: two keys on one identity, and a payment that cannot happen until the second one says so.

Built — on testnet today send() polls its own identity confirmTransfer() Agent · spender key stake 0 · cannot send Proposal, not payment nothing has moved Approver · treasury key stake 80 · never its own Settlement on the stored terms
The split is enforced by key authority at consensus — not by a permission check an API could get wrong, and not by the agent behaving itself. The ledger refuses a compromised agent's payment. Nor can the approver rewrite what it approves: amount, destination and network are read back out of the stored proposal before anything executes. A stake-80 key has dual status — it can raise a transfer as well as confirm one — and the one thing it cannot do is confirm the transfer it raised: the proposal records which key created it, and that key's confirmation is refused. So two agents can check each other with nobody privileged above them, and compromising one key is not enough, even though that key can do both jobs. How it works, in code →
Built

Your key never reaches our servers

Not a policy — a property of the code, everywhere in the stack. Keys are generated in your process and stay there. Transactions are signed locally and posted straight to a chain node, not relayed through an API that could have signed for you.

The one thing our API is asked during wallet setup is which wallet id to claim — a public, unauthenticated read. It hands out ids. It cannot assign one to you; that takes a transaction signed with your own key.

Built

Signed requests, not bearer tokens

An API credential is a secp256k1 keypair you generate; we store only the public half. Each request carries a signature over the route, a timestamp and the exact body — so there is no secret in a header to leak through a log or a proxy, and a signature cannot be replayed against a different endpoint.

Built

Real infrastructure, not a diagram

Four ledger nodes, with onboarding, wallet assignment, transfer and swap written as on-ledger contracts. A public scanner over it. Wallets, an exchange, invoicing and deposit webhooks across two L1 testnets. You can use it now.

Built

Approval as a first-class record

A proposal is not an allow or a deny that vanishes once acted on. It is a durable, on-ledger record of exactly what was proposed and which authority confirmed it, readable on the public scanner afterwards. That is what lets someone accountable sign off on an automated treasury: they can read what it did, and on whose say-so.

03 — Live today

Testnet, and actually running.

Everything below is deployed on public test networks and reachable from this page. None of it handles real funds, and we would rather say so plainly than let a landing page imply otherwise.

Scanner

The ledger and the two L1 testnets it watches, in public: transactions, identities, wallets and deposits.

scanner.varnir.site

Web wallet

Onboard an identity, get a wallet assigned, send and receive — entirely in the browser. The private key is generated there and never leaves.

scanner.varnir.site/wallet

Exchange

Make, take and cancel resting orders that settle atomically on the ledger — both legs in one transaction or neither, instant and gas-free, across assets and across chains. An off-ledger matcher pairs orders; it cannot alter their terms or hold your funds. Sign in the browser or on your paired phone.

scanner.varnir.site/exchange

Invoicing & webhooks

Named, referenced invoices across one or more wallets. Payment is detected from observed deposits, with an HMAC-signed callback at pending and again at confirmed.

USDT and native, both networks

Token registry

Which tokens the network credits is on-ledger state, not a list compiled into the code. Balances are keyed by the token’s L1 contract address, not its ticker — two tokens can share a symbol and never be confused for each other, and adding one is a ledger transaction, not a software release.

read it with listTokens()

Settlement layer

A custom ActiveLedger fork running as a four-node network, with onboarding, wallet assignment, transfer and swap implemented as on-ledger contracts.

Ethereum Sepolia & Tron Nile

SDK

@varnir/chain-client and @varnir/signing — everything the web wallet does, from your own code, signed on your side of the wire.

TypeScript

Playground

Edit and run the SDK against the live testnet, in the browser — onboard an identity, claim a wallet, raise an invoice, and watch each step's real result come back.

playground.varnir.site

Mobile wallet

A native wallet on the same SDK — onboard, send, receive, trade, raise invoices and manage signing keys from the phone. It also signs for the web scanner: pair by QR, and every action that needs a signature is approved on the phone, where the key stays. Works on-device today; not in the app stores yet.

React Native · QR remote signing

04 — Where this is going

The convenience of an exchange, without the custody.

What makes a centralised venue pleasant is that a trade is a bookkeeping entry between two accounts on one system — instant, no gas, no waiting for a block. What makes it dangerous is separate: it holds the assets, so it can lose them, freeze them or spend them. The two come apart, and this rail is already on the right side of both.

Built

Balances already settle internally

A deposit credits an L2 balance on your own identity, written on-ledger as it confirms. Reading your balance is reading your own stream — not asking us what we think you have.

Built

And nobody here holds the keys

The keys that could move those funds on L1 are never whole at Varnir — split across independent node operators, or generated so the key is never assembled anywhere at all. There is no operator override to ask for, and none to steal.

Built

Gas-free transfer between identities

The settlement half of trading is written and reachable: one transaction debits an identity's L2 balance and credits another's, touching no L1 chain and paying no gas. It is an SDK call — transferOnLedger — and the swap contract built on the same move settles both legs of a trade in one transaction, or neither. That is what the exchange above runs on. The swap primitive, in code →

Not built

Partial fills, and treasury-grade trading

A resting order fills whole or not at all; filling part of one at the signed rate is not written. Nor can a treasury identity trade under propose-and-verify yet — an order from one fails closed rather than settling unchecked. And there is no fee in v1: that model is undecided, so nothing is charged.

Agents come first here on purpose. An agent trading against a bounded, approvable balance is a smaller promise than a person's savings, and it exercises the settlement layer for real while the amounts stay proportionate to a testnet. People's own funds are a higher bar, and the bars should be cleared in that order. The full roadmap, including what is missing →

05 — Build on it

Five steps from nothing to a live invoice.

Generate a key, onboard it, claim a wallet, register a scoped signing key, raise an invoice. Every signature happens in your process.

wallet-and-invoice-flow.ts
// 1. A key you generate, and keep.
const phrase  = generateRecoveryPhrase();
const keyPair = derivePrivateKeyFromPhrase(phrase);

// 2. Onboard it. Signed here, posted straight to a chain node.
tx.$sigs.otk = signPayloadBase64(tx.$tx, keyPair.privateKeyHex);
const identity = await sendToAnyNode(tx);

// 3. Claim a wallet, then prove ownership on-chain.
const wallet = await claimWallet('niles', 'trx');
await assignWallet(identity, keyPair, wallet.id);

// 4. Register a separate, scoped signing key.
await registerApiKey(identity, apiKeyPair, ['invoices:create']);

// 5. Raise an invoice — signed request, no bearer token.
const invoice = await createInvoice(apiKeyPair, {
  walletIds: [wallet.id],
  amount: '10',
  currency: 'native',
});

We are early, and we would rather say so.

Varnir is a working testnet with a clear thesis, not a finished product. If AI agents paying on their own behalf is a problem you are thinking about — something to build on, integrate, or argue with — the code is the honest version of the pitch.