DEV Community

Cover image for 683 Tests in Production: Building Bulletproof Self-Hosted AI Wallets
Wallet Guy
Wallet Guy

Posted on

683 Tests in Production: Building Bulletproof Self-Hosted AI Wallets

684 Tests in Production: Building Bulletproof Self-Hosted AI Wallets

Would you trust a third party with your AI agent's private keys? If your answer is "absolutely not," then self-hosted wallet infrastructure is exactly what you're looking for — and WAIaaS ships with 684+ test files to back up that trust with something more reliable than a promise.

Why Test Count Actually Matters for Wallet Software

Here's the thing about wallet infrastructure: bugs don't just produce wrong UI states or broken charts. They lose money. An off-by-one error in a spending limit check, a race condition in transaction pipeline execution, a misconfigured policy that silently allows what it should deny — these aren't academic concerns. They're the reason you should care deeply about test coverage before you hand any codebase the keys to your agent's funds.

WAIaaS is an open-source, self-hosted Wallet-as-a-Service built specifically for AI agents. It's a 15-package monorepo, and across all of those packages — actions, adapters, admin, cli, core, daemon, mcp, openclaw-plugin, push-relay, sdk, shared, skills, wallet-sdk, and more — there are 684+ test files. That's not a vanity metric. That's the foundation you stand on when you run this on your own hardware, with your own keys, and no third party in the loop.

The philosophy here is straightforward: your keys, your server, your rules. No custody risk, no rate limits from hosted services, no vendor lock-in, no wondering what's happening inside someone else's black box.

The Self-Hosting Case: More Practical Than You Think

Running your own infrastructure used to mean real operational overhead. The crypto equivalent of running your own email server — everyone knows it's the right call for privacy and control, but the setup friction historically kept people on hosted alternatives.

WAIaaS changes that calculus. The entire daemon runs in a single Docker container, binds to 127.0.0.1:3100 by default (so it's not exposed to the world out of the box), and the Docker image supports auto-provisioning, Docker Secrets for production deployments, a built-in healthcheck, and runs as a non-root user (UID 1001). Watchtower auto-update support means you can keep your self-hosted instance current without manual intervention.

The quick start is genuinely quick:

git clone https://github.com/waiaas/WAIaaS.git
cd WAIaaS
docker compose up -d
Enter fullscreen mode Exit fullscreen mode

That's it. Three commands. Your wallet infrastructure is running on your machine, in your network, under your control.

If you want auto-provisioning (so you don't have to manually set a master password on first run), you can also do:

docker run -d \
  --name waiaas \
  -p 127.0.0.1:3100:3100 \
  -v waiaas-data:/data \
  -e WAIAAS_AUTO_PROVISION=true \
  ghcr.io/waiaas/waiaas:latest

# Retrieve auto-generated master password
docker exec waiaas cat /data/recovery.key
Enter fullscreen mode Exit fullscreen mode

The recovery key lives on your filesystem, not in someone else's database.

What You're Actually Running

Before we get into the testing story, it helps to understand what WAIaaS is doing under the hood, because the scope explains why the test suite is as large as it is.

The daemon exposes 39 REST API route modules, covering everything from wallet creation and session management to DeFi actions, NFT transfers, and x402 HTTP payment handling. Transactions flow through a 7-stage pipeline: validate → auth → policy → wait → execute → confirm. Every stage is a potential failure point, and every failure point has tests.

The system supports 3 authentication methods:

  • masterAuth (Argon2id) — for system-level operations like creating wallets and setting policies
  • ownerAuth (SIWS/SIWE signatures) — for the human owner approving transactions or exercising the kill switch
  • sessionAuth (JWT HS256) — for the AI agent itself, scoped to what you've explicitly allowed

That three-layer separation isn't incidental. It means a compromised agent session can't create new wallets or change policies. It means the owner can intervene even if the master password is somehow exposed. The 684+ test files cover these boundaries extensively — because the boundaries are the entire point.

The Policy Engine: Where Tests Really Earn Their Keep

The most security-critical component in WAIaaS is the policy engine, and it's also where you most want comprehensive test coverage. WAIaaS implements 21 policy types across 4 security tiers: INSTANT, NOTIFY, DELAY, and APPROVAL.

The default-deny posture means transactions are blocked unless explicitly allowed. If you haven't configured an ALLOWED_TOKENS policy, token transfers don't go through. If you haven't set up a CONTRACT_WHITELIST, contract calls don't execute. This is the right default for wallet software — fail closed, not open.

Here's how you'd set up a spending limit policy that puts the four tiers to work:

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
    }
  }'
Enter fullscreen mode Exit fullscreen mode

With this policy, a $50 transaction executes immediately. A $300 transaction executes immediately but sends a notification. A $1,500 transaction gets queued for 15 minutes — you can cancel it in that window. Anything above $2,000 requires explicit human approval via WalletConnect, Telegram, or Push notification. That's not just configuration — that's a security architecture.

The policy types cover the full surface area of what an AI agent might try to do:

  • PERP_MAX_LEVERAGE and PERP_MAX_POSITION_USD cap your agent's exposure on perpetual futures (Hyperliquid is integrated)
  • LENDING_LTV_LIMIT prevents over-leveraged lending positions on Aave v3 or Kamino
  • X402_ALLOWED_DOMAINS controls which APIs your agent can autonomously pay for via x402
  • REPUTATION_THRESHOLD ties into ERC-8004 onchain agent reputation validation

Each of these policy types has logic that needs to be tested against real transaction schemas. The 684+ test files are covering exactly this kind of correctness.

Dry-Run Before You Fly

One of the more practically useful features for self-hosters is the dry-run API. Before executing any transaction, you can simulate it and see exactly what would happen — including which policy tier it would hit and whether it would be approved, delayed, or denied:

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
  }'
Enter fullscreen mode Exit fullscreen mode

This is the kind of feature that only makes sense when you have the full pipeline running locally. On a hosted service, simulation might be a premium feature or might not reflect the actual execution environment. When you self-host, the simulation runs against the exact same code path as production execution.

Connecting Your AI Agent: MCP in 60 Seconds

If you're running Claude Desktop or any MCP-compatible AI agent framework, connecting it to your self-hosted WAIaaS instance is a config file edit:

{
  "mcpServers": {
    "waiaas": {
      "command": "npx",
      "args": ["-y", "@waiaas/mcp"],
      "env": {
        "WAIAAS_BASE_URL": "http://127.0.0.1:3100",
        "WAIAAS_SESSION_TOKEN": "wai_sess_<your-token>",
        "WAIAAS_DATA_DIR": "~/.waiaas"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The MCP server exposes 45 tools — covering wallet queries, transaction submission, DeFi actions, NFT operations, and x402 payment flows. The WAIAAS_BASE_URL points to your local daemon, meaning your agent's wallet operations never leave your network unless they're actual blockchain transactions.

Or use the CLI shortcut to auto-register all wallets at once:

waiaas mcp setup --all
Enter fullscreen mode Exit fullscreen mode

The TypeScript and Python SDKs

If you're building your own agent rather than using Claude Desktop, both a TypeScript SDK and a Python SDK are available:

import { WAIaaSClient } from '@waiaas/sdk';

const client = new WAIaaSClient({
  baseUrl: 'http://127.0.0.1:3100',
  sessionToken: process.env.WAIAAS_SESSION_TOKEN,
});

const balance = await client.getBalance();
console.log(`${balance.balance} ${balance.symbol}`);
Enter fullscreen mode Exit fullscreen mode
from waiaas import WAIaaSClient

async with WAIaaSClient("http://localhost:3100", "wai_sess_xxx") as client:
    balance = await client.get_balance()
    print(balance.balance, balance.symbol)
Enter fullscreen mode Exit fullscreen mode

Both SDKs communicate exclusively with your local daemon. There's no SDK-level call to any external service. The daemon handles RPC communication with the actual blockchain networks you've configured.

Quick Start: Self-Hosted in Five Steps

If you want to go from zero to a running wallet infrastructure with proper policies in place, here's the minimal path:

Step 1: Start the daemon

git clone https://github.com/waiaas/WAIaaS.git
cd WAIaaS
docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Step 2: Install the CLI and initialize

npm install -g @waiaas/cli
waiaas init
waiaas start
Enter fullscreen mode Exit fullscreen mode

Step 3: Create wallets and sessions in one step

waiaas quickset --mode mainnet
Enter fullscreen mode Exit fullscreen mode

Step 4: Set up a spending limit 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": 10,
      "notify_max_usd": 100,
      "delay_max_usd": 1000,
      "delay_seconds": 300,
      "daily_limit_usd": 500,
      "monthly_limit_usd": 5000
    }
  }'
Enter fullscreen mode Exit fullscreen mode

Step 5: Connect Claude Desktop

waiaas mcp setup --all
Enter fullscreen mode Exit fullscreen mode

Open Claude Desktop, and your agent now has a self-hosted wallet it can use — constrained by policies you defined, running on infrastructure you control.

For Production: Docker Secrets

If you're running this on a homelab server or a VPS rather than a local machine, the secrets overlay is worth using:

mkdir -p secrets
echo "your-secure-password" > secrets/master_password.txt
chmod 600 secrets/master_password.txt

docker compose -f docker-compose.yml -f docker-compose.secrets.yml up -d
Enter fullscreen mode Exit fullscreen mode

This uses Docker Secrets to inject the master password rather than passing it as an environment variable, which keeps it out of docker inspect output and process listings.

The Open Source Guarantee

The 684+ test files aren't just a quality signal — they're also an auditability signal. Every test describes expected behavior explicitly. If you're privacy-conscious enough to self-host, you're probably also the kind of person who wants to read what the code actually does rather than taking a vendor's word for it.

WAIaaS is open source. The codebase is on GitHub. The test suite runs against the same code you're deploying. If a test passes in CI, the same behavior is what you get when you docker compose up.

That's the deal with self-hosted software done right: you give up convenience at the margin, and you gain certainty, sovereignty, and the ability to audit everything that touches your keys.

What's Next

The best next step is getting your own instance running and exploring the Admin Web UI at /admin, which gives you a visual interface for wallet management, session control, the policy editor, and DeFi positions — all pointed at your local daemon. After that, the OpenAPI interactive reference at /reference is the fastest way to explore the full API surface across all 39 route modules.

Explore the full codebase and contribute on GitHub, and learn more about the project at waiaas.ai.

Top comments (0)