Security model
Non-custodial by construction, enforced onchain, tested adversarially.
The threat model, in one pass
The system is designed against named threats, each with a structural answer, not a promise.
- Prompt injection. NFT metadata, market data and chat are all untrusted input. It does not matter what any of them say: no tool the model can reach raises a limit, changes a policy, or names a recipient. A fully successful injection can at most cause an in-envelope bet you can see.
- Wallet compromise, ours. A stolen session key can bet inside the caps until revoked or expired, and nothing more. It cannot withdraw, cannot re-aim funds, cannot raise its own limits.
- Replay. Every session signature binds the account, target, value, data, a nonce and the chain. Signatures cannot be replayed.
- Double execution. The transaction hash is persisted before broadcast and every request carries an idempotency key. A crashed worker resumes by reading the chain, not by signing again.
- NFT transfer mid-flight. The registry locks the agent the moment ownership diverges, the session is revoked onchain, and open bets settle safely instead of being torn up.
The four contracts
- AgentRegistry. Binds an NFT to an agent and its account, and holds the transfer trip-wire. Anyone, not just us, can prove an ownership change and force the lock.
- AgentAccountFactory. Deploys each agent’s smart account deterministically, with the owner set to you and the policy module installed, in one atomic sequence.
- AgentPolicyModule. The load-bearing contract. Every agent transaction passes through it: session validity, allowlist, per-transaction cap, rolling daily cap, rate limit, replay protection, pause. It is not an owner of the account and can never become one.
- ProviderRegistry. The venue allowlist, per contract and per function, governed behind a timelock. Additions wait 48 hours. Removals and pause are immediate.
The gauntlet on every transaction
When the agent moves money, the onchain module runs the full checklist before anything executes: the session is not expired or revoked, the signature is valid and unreplayed, the NFT binding is not locked, the target and function are allowlisted, the value fits the per-transaction cap and the rolling daily cap, the hourly rate limit holds, and the system is not paused.
Fail any one, and the transaction reverts. This all happens onchain, which means it holds even if every server behind the product is compromised or gone.
Invariants under test
These are not aspirations. Each one is an explicit test, and the suite is currently 27 Foundry tests, all green, including a 5000-run fuzz on the daily cap.
- An attacker cannot change policy. Only the account owner can, and only by signing.
- A session key cannot raise its own caps, extend its own expiry, or bypass its rate limit.
- The owner withdraws under any state, including paused and locked. This is the one that must never regress.
- Signatures cannot be replayed, on this chain or any other.
- Execution uses call, never delegatecall. The module cannot be tricked into acting as the account.
- The daily spend accounting cannot be reset by any agent-reachable path.
The model never signs
The language model has no financial output at all. It writes the agent’s words, after the fact, from the agent’s settled record; it cannot emit an action, transaction data, a signature or an address.
Deterministic code makes every decision, encodes it from a fixed set of templates, checks it against the policy, and hands it to the signer. There is no code path from model output to the chain.
Key isolation
Session keys live in a dedicated signing service, isolated from the web application in its own network zone. The key is never in the app, never in the database, never in an environment variable the product code can read. The chat API holds no signing key at all.
The signer does not trust its callers. It re-derives the policy verdict independently before signing anything, so a compromised worker cannot talk it into an out-of-policy transaction. Sessions expire on a short cycle and rotate automatically, bounding any compromise in time.
Emergency pause
A guardian can pause agent execution instantly across the system. The pause blocks exactly one thing: agents placing bets. It cannot touch funds, and it cannot touch withdrawals, because the owner’s exit path does not pass through the pausable contract at all.
That separation is the point. An emergency stop that could also freeze user money would turn an incident into a custody failure, so it is built to be unable to.
Trust the math, not the team
The safety story does not rest on trusting the team. It rests on the invariants above, enforced on chain and covered by 34 Foundry tests, and on the fact that you can always withdraw from your own Safe — nobody privileged can move your funds.
The builds are reproducible: anyone can compile the source and verify the deployed bytecode matches it. Trust the math, not the team — that is the whole posture. Betting on an onchain casino is real risk with real losses; nothing here removes it.