Skip to content
DocsArchitecture

DocsHow it works

Architecture

The browser, the Next.js app, the agent worker, the Monad contracts, Envio HyperSync and Postgres, and which of them decides.

Diagram. The same diagram is written out as text below it.
Open the diagram full size
The diagram as text
flowchart TB
  subgraph Browser
    P[Person: Google or email] --> EW[Privy embedded wallet]
    N[Nini: Jev picks the service, code locks the amount, Kimi writes the reply] -. draft to confirm .-> P
  end
  subgraph Vercel [Next.js app]
    API[API routes: check, build, relay the signed request]
  end
  subgraph Railway [Agent worker]
    AG[One lane per agent: rule due, intent saved, then signed]
  end
  EW -- approves the exact request --> API
  API -- sponsor: true --> SP[Privy gas sponsorship: EIP-7702 + ERC-4337]
  AG -- agent's Privy server wallet --> SP
  SP --> ACC
  subgraph Monad
    ACC[NuntioAccount: funds + one policy per spender]
    REG[ServiceRegistry: service id to payee]
    Q[ApprovalQueue: requests the owner decides]
    ACC -- resolve --> REG
    ACC -. refused: file a request .-> Q
    Q -- approved --> ACC
  end
  ACC -- AUSD --> M[Merchant payee]
  HS[Envio HyperSync] -- payment and request events --> API
  DB[(Neon Postgres: teams, agents, rules, intents)] --- API
  DB --- AG

#The parts

PartWhere it runsWhat it doesCan it authorize a payment?
BrowserYour deviceSigns you in through Privy, holds your embedded wallet's session, and approves each request you sendNo
Web appNext.js 16 on VercelThe product, the public site and /stats, and the API routes that check, build and relay signed requestsNo
Agent workerRailwayRuns agents: turns due rules into intents, signs with each agent's own wallet, reconciles from the chainNo
ContractsMonadNuntioAccount, ApprovalQueue, ServiceRegistry, NuntioTeamFactoryYes. Only NuntioAccount moves money
PrivyPrivy's serviceSign-in, embedded wallets, agent server wallets and their policies, gas sponsorshipNo, though its policy can refuse first
Envio HyperSyncEnvio's serviceFinds payment and request events across every account deploymentNo
PostgresNeon, one database per networkTeams, invites, join requests, Sandbox members, agents, rules, intents, attempts and an audit trailNo
Jev and KimiTheir providersNini's two model roles: Jev picks a registered service, Kimi writes the replyNo

#One build per network

The same code is deployed twice. The network is fixed when the app is built:

SiteNetworkVercel project
nuntio.financeMonad mainnet, chain 143nuntio-mainnet
testnet.nuntio.financeMonad testnet, chain 10143nuntio-web

Every address, the list of services, the copy and the explorer links follow from that choice, so a testnet build cannot show a mainnet address or the reverse. The worker is also deployed once per network, each with its own database.

#How a payment moves

  1. It starts as an intent. A person presses Pay, or an agent's rule comes due. Nini can draft one from a sentence, but it only picks a registered service, and code reads the amount.
  2. The wallet signs, the sponsor pays gas. A person's embedded wallet approves the exact request in the browser. The server checks it and relays it with sponsor: true. An agent signs with its own Privy server wallet, bounded by a Privy policy. The contract sees the spender's wallet as the caller, not the sponsor.
  3. The contract decides. NuntioAccount.pay checks the spender's status, expiry, approved services, cap, allowance and payment count, resolves the payee from ServiceRegistry, transfers AUSD and records the amount it measured leaving the account.
  4. Outside the policy, the owner decides. A person can file the refused payment on ApprovalQueue. The owner approves or declines, and an approved request settles once through paySettled. The agent worker files no requests yet, so an agent's refused payment ends there.
  5. Every receipt is read back. History is found through Envio HyperSync, and each payment is confirmed from its receipt over RPC. A failed read shows as unknown, never as zero.

#Rules the system keeps

  • The contract is the only authority. The app's checks and Privy's policies refuse earlier with better messages. Each layer refuses on its own.
  • No spending function takes an address. Payees come from the registry at the moment of payment.
  • The database never authorizes. It remembers. Balances in it, and cached figures on screens, are for display only. Every money path reads the chain.
  • RPC is checked. The app and the worker use a list of RPC endpoints with fallback, and refuse any endpoint that answers for another chain.
  • A failed read is unknown, never zero. A dash on screen means "we could not read this".