21 Policy Types for Self-Hosted Crypto Wallets: Complete Risk Management
Would you trust a third party with your AI agent's private keys? If you're building autonomous agents that move real money, the question of who controls the wallet matters as much as the policy rules protecting it. WAIaaS is an open-source, self-hosted Wallet-as-a-Service that lets you run your own wallet infrastructure — and back it with a policy engine covering 21 distinct risk controls, all enforced locally on your own server.
Why Risk Management for AI Agents Is Different
Traditional wallet security assumes a human is making every decision. A human checks the recipient address. A human notices when a transaction looks wrong. A human stops before clicking send on a $10,000 transfer at 3 AM.
AI agents don't do any of that.
When you give an autonomous agent a session token, you're handing it the ability to sign and broadcast transactions without any of those human checkpoints. That's the whole point — but it's also the risk. An agent following bad instructions, a compromised session token, or a misconfigured DeFi strategy can drain funds faster than any human operator can react. The policy engine isn't a nice-to-have. It's the layer between "agent does something clever" and "agent does something catastrophic."
The self-hosted angle matters here too. When you run WAIaaS on your own server, your private keys never leave your infrastructure. The policy engine enforcing your rules runs on your machine. No hosted service is making decisions about your transactions, rate-limiting your agent's API calls, or holding your keys in a multi-tenant environment. Your server, your rules, full stop.
The Default-Deny Foundation
Before diving into the 21 policy types, it's worth understanding the foundational principle: default-deny.
If you don't configure an ALLOWED_TOKENS policy, token transfers are blocked. If you don't configure a CONTRACT_WHITELIST, contract calls are blocked. The absence of a permission is itself a denial. This is the opposite of most wallet setups where everything is allowed until you explicitly restrict it.
This matters for agents specifically because agents will try things you didn't anticipate. A default-allow system means your agent can do anything until you catch and block it. A default-deny system means your agent can only do what you've explicitly permitted.
The 4 Security Tiers
Every transaction WAIaaS processes gets assigned to one of four security tiers before execution:
- INSTANT — Execute immediately, no notification
- NOTIFY — Execute immediately, but send you a notification
- DELAY — Queue the transaction for a configurable number of seconds, then execute (you can cancel during this window)
- APPROVAL — Block until you explicitly approve via WalletConnect, Telegram, or push notification
The SPENDING_LIMIT policy is what maps transaction amounts to these tiers. Here's a practical example — a policy that lets your agent handle small routine payments instantly, notifies you on medium transactions, delays large ones (giving you a cancellation window), and requires your explicit sign-off on anything significant:
curl -X POST http://localhost:3100/v1/policies \
-H 'Content-Type: application/json' \
-H 'X-Master-Password: <password>' \
-d '{
"walletId": "<wallet-uuid>",
"type": "SPENDING_LIMIT",
"rules": {
"instant_max_usd": 10,
"notify_max_usd": 100,
"delay_max_usd": 1000,
"delay_seconds": 300,
"daily_limit_usd": 500,
"monthly_limit_usd": 5000
}
}'
With this config: anything under $10 executes immediately, $10–$100 executes with a notification, $100–$1,000 gets queued for 5 minutes before executing, and anything over $1,000 waits for your manual approval. The daily cap of $500 and monthly cap of $5,000 add an aggregate safety net on top.
All 21 Policy Types
Here's what the full policy library covers:
Transfer Controls
SPENDING_LIMIT — The core 4-tier security policy described above. Maps USD amounts to INSTANT/NOTIFY/DELAY/APPROVAL tiers with daily and monthly aggregate limits.
WHITELIST — Restricts which recipient addresses your agent can send funds to. If an address isn't on the list, the transaction is blocked:
{"allowed_addresses": ["<address1>", "<address2>"]}
ALLOWED_TOKENS — Default-deny whitelist of which tokens your agent can transfer. Your agent can only move tokens explicitly listed here:
{"tokens": [{"address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "symbol": "USDC", "chain": "solana"}]}
RATE_LIMIT — Caps the number of transactions your agent can submit per period:
{"maxTransactions": 10, "period": "hourly"}
TIME_RESTRICTION — Limits when transactions can execute. Useful if your agent should only operate during business hours:
{"allowedHours": {"start": 9, "end": 17}, "timezone": "UTC"}
ALLOWED_NETWORKS — Restricts which chains your agent can transact on:
{"networks": [{"network": "ethereum-mainnet"}, {"network": "solana-mainnet"}]}
Contract Interaction Controls
CONTRACT_WHITELIST — Default-deny whitelist of smart contracts your agent can call. If the contract address isn't listed, the call is blocked:
{"contracts": [{"address": "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4", "name": "Jupiter", "chain": "solana"}]}
METHOD_WHITELIST — Restricts which function selectors (4-byte method IDs) the agent can call on contracts. Useful when you want to allow a contract but only specific functions on it.
APPROVED_SPENDERS — Default-deny whitelist controlling which addresses your agent can grant token approval to:
{"spenders": [{"address": "0xDEF1...", "name": "Uniswap Router", "maxAmount": "1000000000"}]}
APPROVE_AMOUNT_LIMIT — Sets a cap on approval amounts and can block unlimited approvals (the approve(MAX_UINT256) pattern that's common but risky).
APPROVE_TIER_OVERRIDE — Forces approval transactions into a specific security tier regardless of amount. You might want all approve() calls to require human sign-off even if the amount would normally be INSTANT.
DeFi-Specific Controls
LENDING_LTV_LIMIT — Caps the loan-to-value ratio your agent can take on in lending protocols like Aave. Prevents your agent from over-leveraging borrowed positions.
LENDING_ASSET_WHITELIST — Restricts which assets your agent can supply or borrow in lending protocols.
PERP_MAX_LEVERAGE — Sets the maximum leverage multiplier for perpetual futures positions on protocols like Hyperliquid.
PERP_MAX_POSITION_USD — Caps the total USD size of any single perpetual position.
PERP_ALLOWED_MARKETS — Restricts which perpetual futures markets your agent can trade.
VENUE_WHITELIST — Controls which trading venues or DEXes your agent can route through.
ACTION_CATEGORY_LIMIT — Applies aggregate limits across DeFi action categories (lending, staking, trading, etc.).
Emerging Protocol Controls
X402_ALLOWED_DOMAINS — WAIaaS supports the x402 HTTP payment protocol, where AI agents automatically pay for API calls. This policy whitelists which domains your agent can make automatic payments to:
{"domains": ["api.example.com", "*.openai.com"]}
REPUTATION_THRESHOLD — Sets a minimum ERC-8004 onchain reputation score required for counterparties in agent-to-agent interactions.
ERC8128_ALLOWED_DOMAINS — Restricts which domains your agent can sign HTTP requests for under the ERC-8128 protocol.
Approving a Transaction as the Owner
When a transaction hits the APPROVAL tier, your agent's execution is paused. The transaction waits until you act on it. Once you've reviewed and decided to proceed, approving looks like this:
curl -X POST http://127.0.0.1:3100/v1/transactions/<tx-id>/approve \
-H "X-Owner-Signature: <ed25519-or-secp256k1-signature>" \
-H "X-Owner-Message: <signed-message>"
The ownerAuth layer uses your own keypair — the same address you used when setting up owner authentication. Nothing about this approval touches a third-party service. The signature verification happens on your server.
Testing Before You Commit
Before you deploy a policy setup to a live agent, you can simulate transactions to see exactly how they'd be handled. The dryRun flag runs the full policy evaluation pipeline without broadcasting anything:
curl -X POST http://127.0.0.1:3100/v1/transactions/send \
-H "Content-Type: application/json" \
-H "Authorization: Bearer wai_sess_<token>" \
-d '{
"type": "TRANSFER",
"to": "recipient-address",
"amount": "0.1",
"dryRun": true
}'
This is how you validate that your SPENDING_LIMIT thresholds work the way you intended, that your WHITELIST covers the right addresses, and that your ALLOWED_TOKENS list includes everything your agent legitimately needs — before any real funds are at risk.
What a Policy Denial Looks Like
When a transaction is blocked by a policy, the API returns a structured error rather than silently failing. Your agent (or your monitoring) gets something like:
{
"error": {
"code": "POLICY_DENIED",
"message": "Transaction denied by SPENDING_LIMIT policy",
"domain": "POLICY",
"retryable": false
}
}
The retryable: false flag tells the agent not to keep hammering the endpoint. The error code tells you exactly which policy triggered the block, so you can audit your configuration.
Getting It Running
The fastest path from zero to a self-hosted WAIaaS instance with policies configured:
Step 1: Start the daemon
git clone https://github.com/minhoyoo-iotrust/WAIaaS.git
cd WAIaaS
docker compose up -d
The Docker image is ghcr.io/minhoyoo-iotrust/waiaas:latest, and it binds to 127.0.0.1:3100 by default — local only, not exposed to the network.
Step 2: Initialize and create a wallet
npm install -g @waiaas/cli
waiaas init
waiaas start
Or use waiaas quickset --mode mainnet to create wallets and sessions in one step.
Step 3: Create your spending policy
curl -X POST http://127.0.0.1:3100/v1/policies \
-H "Content-Type: application/json" \
-H "X-Master-Password: my-secret-password" \
-d '{
"walletId": "<wallet-uuid>",
"type": "SPENDING_LIMIT",
"rules": {
"instant_max_usd": 100,
"notify_max_usd": 500,
"delay_max_usd": 2000,
"delay_seconds": 900,
"daily_limit_usd": 5000
}
}'
Step 4: Add token and contract whitelists
Layer ALLOWED_TOKENS and CONTRACT_WHITELIST on top of your spending limit. Remember: without these, token transfers and contract calls are blocked by default.
Step 5: Create a session for your agent
curl -X POST http://127.0.0.1:3100/v1/sessions \
-H "Content-Type: application/json" \
-H "X-Master-Password: my-secret-password" \
-d '{"walletId": "<wallet-uuid>"}'
Your agent gets a wai_sess_... token scoped to that wallet and governed by every policy you've configured.
The Philosophy Behind Self-Hosting This
Running WAIaaS is philosophically similar to running your own email server. It's more setup than using a hosted service. But you get things a hosted service structurally cannot give you: your private keys live on your hardware, your policy engine enforces rules without calling home, and your agent's transaction history stays on your infrastructure.
The 21 policy types aren't about making things complicated. They're about giving you the specific controls you actually need — whether that's a simple spending cap for a personal agent or a layered DeFi risk framework for something managing real capital. Start with SPENDING_LIMIT and ALLOWED_TOKENS. Add the DeFi controls when you're running lending or futures strategies. The rest are there when you need them.
What's Next
The interactive API reference at http://127.0.0.1:3100/reference documents every policy type with request/response schemas you can try directly in the browser. The admin UI at /admin includes a policy editor for managing policies without writing curl commands. If you're wiring this up to Claude or another MCP-compatible agent, the 45 MCP tools include get-policies so your agent can introspect its own policy configuration at runtime.
Grab the source, open an issue, or star the project on GitHub:
- GitHub: https://github.com/minhoyoo-iotrust/WAIaaS
- Official site: https://waiaas.ai
Top comments (0)