DocsSecurity
Limits and known risks
What Nuntio does not protect you from, what is not built yet, and the contracts' known limitations.
Stated plainly, because a limit nobody wrote down is one somebody will assume away.
#The big ones
- Not independently audited. The contracts went through internal adversarial reviews, a re-review and a live migration on testnet, and nothing more. They are live on mainnet with real AUSD. Keep balances small.
- The owner is fully trusted. An owner sets every policy, approves every exception and can withdraw the whole balance. The contracts protect spenders from each other and from the assistant, not from the owner.
- Nuntio's main account is owned by a single key. On each network, the main account's owner is the protocol admin key, a single key rather than a multisig. When the app acts as the main account's owner, the web server signs with it, scoped so it cannot call
payor choose a recipient. Teams are owned and signed for by the people who created them. - The mainnet service registry is owned by one key, the mainnet protocol admin, not a multisig. Whoever holds it can register, suspend or re-point a service, which changes where future payments to that service go. Moving it to a 2-of-3 Safe is planned and not done.
- On mainnet the only seller is Nuntio. The things to buy are AI compute on 0G and Monad data bought over x402, sold by Nuntio's own commerce gateway and labelled "run by Nuntio": a payment for a run goes to Nuntio. The demo services Nuntio ran before, which paid its own wallet
0x457e46E52bE1b4A05e3f49d07e58d845ec15CE31, are all suspended. - No cap across accounts. Each team's limits are its owner's. The only brake across all accounts is suspending a service in the registry.
#Dependencies you rely on
- Gas sponsorship depends on a prepaid Privy balance. When it runs out, a sponsored write is refused before it is sent, and the app says nothing moved.
- Sign-in and wallets depend on Privy. Payments, team owner actions and agent payments are all signed through Privy.
- AUSD is issued by Agora. The token can freeze an address, and a frozen address cannot move AUSD.
- Nini depends on its model providers. If Jev is down, Nini drafts nothing. You can always pay from the New payment screen.
- History depends on Envio HyperSync and Monad RPC. When either cannot be read, the app says so instead of showing an empty history.
#Contract limitations
- A merchant controls its own payout address. The service's current payee can call
setPayee. An in-policypayfollows the registry when it runs, so a merchant whose key is stolen redirects its future payments until the registry owner suspends or re-points it. Approved exceptions are protected:paySettledrefuses a changed payee. - The registry's ownership can be renounced.
NuntioAccountrefuses to renounce ownership, butServiceRegistryinherits OpenZeppelin's, which works. After a renounce nobody could register or suspend a service. This is handled by operating practice, not by the contract. granton an existing spender resets their period: what they spent and how many payments they made go back to zero. The app never re-sends agrantover a policy that is in place.setLimitsoverwrites all three limits. A zeroed field removes that limit, because zero means no limit.setAllowanceexists for the common edit. The app does not call either today.withdrawemits no event of its own. The AUSDTransferfrom the account to the owner is the only record.importSpentKeysannounces how many keys it imported, not which. A migrated account's ledger cannot be rebuilt from its own events alone. The previous account'sPaidevents hold the keys.- An approval is a commitment once it is mined.
paySettledcan be called by anyone, so an approved request can be settled at any moment before its deadline. Withdrawing it races settlement rather than guaranteeing it. - A spender whose policy has expired can still file a request. It can never settle, because settlement checks the expiry.
- The measurement cannot see a fee taken on the recipient's side.
Paid.amountis what left the account. AUSD charges no such fee. - A team that is never accepted keeps whatever is sent to it. Until its person accepts ownership, nobody can withdraw, and the factory has no rescue function.
- Testnet key separation is incomplete. The testnet protocol admin is the deployer key. Mainnet separates them.
- Source verification on the block explorers is not recorded yet. The deployed bytecode matches the source, and the repository has a read-only check for it.
#Not built yet
- Agents on team accounts. During the beta, agents run on Nuntio's main account only.
- Approval requests from agents. When the contract would refuse an agent's payment, the worker records the refusal. It does not file a request.
- Editing an active spender's limits in the app. Revoke and add them again.
- A withdraw screen. The contract supports
withdrawby the owner. - Leaving or handing over a team, or more than one team per person.
- Metered, per-request charging. Not built.
- Real third-party merchants on mainnet. Not yet.
#Also worth knowing
- A provisional receipt is provisional. Monad's
latestblock is speculative. Check that a receipt's block is at or belowfinalizedbefore anything irreversible. - App budgets are per server instance. The limits on sponsored sends and Nini drafts protect Nuntio's credits. The app keeps their counters in memory on each instance.
- The earlier VERSION 1 contracts remain on testnet, emptied. The most recent of them is paused.