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.
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
| Part | Where it runs | What it does | Can it authorize a payment? |
|---|---|---|---|
| Browser | Your device | Signs you in through Privy, holds your embedded wallet's session, and approves each request you send | No |
| Web app | Next.js 16 on Vercel | The product, the public site and /stats, and the API routes that check, build and relay signed requests | No |
| Agent worker | Railway | Runs agents: turns due rules into intents, signs with each agent's own wallet, reconciles from the chain | No |
| Contracts | Monad | NuntioAccount, ApprovalQueue, ServiceRegistry, NuntioTeamFactory | Yes. Only NuntioAccount moves money |
| Privy | Privy's service | Sign-in, embedded wallets, agent server wallets and their policies, gas sponsorship | No, though its policy can refuse first |
| Envio HyperSync | Envio's service | Finds payment and request events across every account deployment | No |
| Postgres | Neon, one database per network | Teams, invites, join requests, Sandbox members, agents, rules, intents, attempts and an audit trail | No |
| Jev and Kimi | Their providers | Nini's two model roles: Jev picks a registered service, Kimi writes the reply | No |
#One build per network
The same code is deployed twice. The network is fixed when the app is built:
| Site | Network | Vercel project |
|---|---|---|
| nuntio.finance | Monad mainnet, chain 143 | nuntio-mainnet |
| testnet.nuntio.finance | Monad testnet, chain 10143 | nuntio-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
- 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.
- 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. - The contract decides.
NuntioAccount.paychecks the spender's status, expiry, approved services, cap, allowance and payment count, resolves the payee fromServiceRegistry, transfers AUSD and records the amount it measured leaving the account. - 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 throughpaySettled. The agent worker files no requests yet, so an agent's refused payment ends there. - 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".