Production Docker for Monorepo AI Wallets: 15-Package Architecture Deployment Guide
Would you trust a third party with your AI agent's private keys? If you're building autonomous agents that touch real money, that question matters more than most people realize. Self-hosting your wallet infrastructure means your keys never leave your server, your agent never hits someone else's rate limits, and nobody can freeze your wallet because their compliance team had a bad morning.
Why Self-Hosting an AI Wallet Actually Makes Sense Now
The rise of agentic AI has created a new problem: agents need to transact, but wallets were designed for humans. Hosted wallet services solve the UX problem but reintroduce the custody problem. You're back to trusting a third party with your keys — except now it's not just your funds at risk, it's your agent's entire operational capability.
Running your own wallet infrastructure used to mean cobbling together signing libraries, key management, policy enforcement, and monitoring from scratch. That's weeks of work before you write a single line of agent logic. WAIaaS is an open-source, self-hosted Wallet-as-a-Service built specifically for AI agents. It's the crypto equivalent of running your own email server — except this time the setup is actually manageable, and you get a production-grade 15-package monorepo out of the box.
The tradeoff is real: you own the ops burden. But you also own the keys, the data, the uptime decisions, and the rate limits. For privacy-conscious builders and homelab enthusiasts, that's not a tradeoff — it's the whole point.
What You're Actually Deploying
Before diving into the Docker setup, it helps to understand what's running under the hood. WAIaaS is a 15-package monorepo: actions, adapters, admin, cli, core, daemon, desktop-spike, e2e-tests, mcp, openclaw-plugin, push-relay, sdk, shared, skills, wallet-sdk.
The piece that matters most for deployment is the daemon — the core REST API server with 39 route modules that handles everything from wallet creation to DeFi execution. Behind it sits the mcp package, which exposes 45 MCP tools so AI agents like Claude can interact with your wallets through natural language. The push-relay is a separate service for routing transaction approval notifications.
In Docker terms, this means 2 Docker images: the main waiaas/daemon and the push-relay. For most self-hosters, you'll start with just the daemon and add the relay later when you need mobile approval notifications.
The daemon listens on 127.0.0.1:3100 by default — bound to localhost only, which is exactly the right default for a self-hosted setup. Nothing is accidentally exposed to the internet.
The Fastest Path to Running Your Own AI Wallet Server
If you want to get something running before you read the rest of this post:
git clone https://github.com/waiaas/WAIaaS.git
cd WAIaaS
docker compose up -d
That's it. Three commands. The Docker image is waiaas/daemon:latest and the compose file handles the rest. Your wallet server is now running at http://127.0.0.1:3100.
If you don't want to clone the repo and just want to spin up a container:
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
The WAIAAS_AUTO_PROVISION=true flag tells the entrypoint to generate a random master password on first boot and write it to /data/recovery.key. This is the self-hosted friendly path — no need to pre-configure a password, just retrieve it after startup and store it somewhere safe.
Understanding the Production Compose File
The Docker Compose configuration that ships with WAIaaS is production-ready out of the box:
services:
daemon:
image: ghcr.io/waiaas/waiaas:latest
container_name: waiaas-daemon
ports:
- "127.0.0.1:3100:3100"
volumes:
- waiaas-data:/data
environment:
- WAIAAS_DATA_DIR=/data
- WAIAAS_DAEMON_HOSTNAME=0.0.0.0
env_file:
- path: .env
required: false
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3100/health"]
interval: 30s
timeout: 5s
start_period: 10s
retries: 3
volumes:
waiaas-data:
driver: local
A few things worth calling out for self-hosters:
Port binding to 127.0.0.1:3100:3100 — The daemon is bound to localhost, not 0.0.0.0. If you need external access, put a reverse proxy in front. Don't just change this to 0.0.0.0 without understanding the security implications.
Named volume for /data — All wallet keys, session tokens, and configuration live in the waiaas-data volume. docker compose down preserves this. Only docker compose down -v deletes it. This is the right behavior for a wallet server.
restart: unless-stopped — Your agent wallet server comes back after reboots and container crashes without manual intervention.
Built-in healthcheck — The compose file includes a healthcheck against /health. Docker will mark the container unhealthy and restart it if the API stops responding.
Secrets Management for Production
For a homelab or personal project, environment variables in a .env file are fine. For anything that touches real funds at scale, WAIaaS ships with a Docker Secrets overlay specifically for production:
# Create secret files
mkdir -p secrets
echo "your-secure-password" > secrets/master_password.txt
chmod 600 secrets/master_password.txt
# Deploy with secrets overlay
docker compose -f docker-compose.yml -f docker-compose.secrets.yml up -d
The docker-compose.secrets.yml overlay file wires Docker Secrets into the container without putting sensitive values in environment variables or compose files. The entrypoint script handles reading from Docker Secrets automatically.
This is the right pattern for self-hosted infrastructure that you'd actually trust with production keys. Secrets don't show up in docker inspect output, process lists, or compose file commits.
RPC Endpoints: The Other Key Configuration
The daemon needs blockchain RPC endpoints to actually submit transactions. The key environment variables:
WAIAAS_RPC_SOLANA_MAINNET=<url> # Solana mainnet RPC endpoint
WAIAAS_RPC_EVM_ETHEREUM_MAINNET=<url> # Ethereum mainnet RPC endpoint
WAIaaS supports 2 chain types (Solana and EVM) across 18 networks. For each network you want to use, you'll point it at an RPC endpoint. Self-hosters who are serious about sovereignty will run their own RPC nodes here too — but for getting started, any standard RPC endpoint works.
The full set of key environment variables:
WAIAAS_AUTO_PROVISION=true # Auto-generate master password on first start
WAIAAS_DAEMON_PORT=3100 # Listening port
WAIAAS_DAEMON_HOSTNAME=0.0.0.0 # Bind address (inside container)
WAIAAS_DAEMON_LOG_LEVEL=info # Log level (trace/debug/info/warn/error)
WAIAAS_DATA_DIR=/data # Data directory
Three Auth Layers, One Deployment
Once the container is running, you interact with it through three distinct authentication roles. Understanding these is key to operating a self-hosted wallet server correctly:
masterAuth — This is you, the server operator. Used for creating wallets, managing sessions, and setting policies. Never give this to an AI agent.
# Create a wallet
curl -X POST http://127.0.0.1:3100/v1/wallets \
-H "Content-Type: application/json" \
-H "X-Master-Password: my-secret-password" \
-d '{"name": "trading-wallet", "chain": "solana", "environment": "mainnet"}'
sessionAuth — This is the token your AI agent uses. Scoped, time-limited, and constrained by whatever policies you've set.
# Check balance (what your agent does)
curl http://127.0.0.1:3100/v1/wallet/balance \
-H "Authorization: Bearer wai_sess_eyJhbGciOiJIUzI1NiJ9..."
ownerAuth — This is you, the fund owner, approving transactions that exceed your policy thresholds. Signed with your wallet key.
# Approve a pending transaction
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 three-layer design means your agent has exactly the permissions it needs and nothing more. If a session token leaks, the attacker is still constrained by your policies.
Locking It Down with Policies
The policy engine is where self-hosting really pays off. You define the rules, and the daemon enforces them — no exceptions, no overrides, no "contact support to change your limits."
There are 21 policy types with 4 security tiers: INSTANT, NOTIFY, DELAY, and APPROVAL. The default is deny — transactions are blocked unless you've explicitly allowed them. Here's a realistic spending limit policy for an autonomous trading agent:
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
}
}'
This says: anything under $100 executes immediately, $100-$500 executes but notifies you, $500-$2000 waits 15 minutes (and you can cancel), and anything over $2000 requires your explicit approval. You didn't have to call anyone to set this up. There's no support ticket. It's just configuration on your own server.
The policy system also includes DeFi-specific controls — PERP_MAX_LEVERAGE, LENDING_LTV_LIMIT, PERP_MAX_POSITION_USD — that let you constrain what your agent can do in protocols like Hyperliquid or Aave without blocking DeFi access entirely.
Connecting Your AI Agent
Once your server is running and your policies are set, connecting an AI agent takes two steps.
Via MCP (for Claude Desktop):
waiaas mcp setup --all # Auto-register all wallets with Claude Desktop
Or manually in claude_desktop_config.json:
{
"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"
}
}
}
}
The MCP server exposes 45 tools to Claude — everything from get-balance to send-token to get-defi-positions. Your self-hosted daemon is the backend; the MCP server is just the translation layer.
Via the TypeScript or Python SDK for programmatic agents:
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}`);
The SDK points at your server. Not WAIaaS's servers — yours.
Daily Operations
A few commands you'll use regularly:
docker compose up -d # Start daemon
docker compose logs -f # Follow logs
docker compose down # Stop (data preserved in named volume)
docker compose down -v # Stop + delete data volume
The daemon also ships with an Admin Web UI at /admin — wallet management, session control, policy editor, DeFi positions dashboard, and notification configuration, all in a browser. And the full OpenAPI 3.0 spec is available at /doc with an interactive API reference at /reference if you want to explore the 39 API routes directly.
For teams or more complex setups, the CLI has 20 commands covering backup, restore, status monitoring, and wallet management — backup create, backup list, restore, status, stop, update, and more.
The Philosophy, One More Time
Hosted services are convenient. They handle ops, they provide support, and they let you focus on building. But for an AI agent that autonomously moves funds, convenience and custody are in tension. A hosted wallet service that goes down takes your agent with it. A hosted service with rate limits throttles your agent. A hosted service with compliance controls can freeze your agent's wallet.
Self-hosting WAIaaS means you've eliminated that dependency. Your server, your keys, your uptime decisions, your rate limits. The 684+ test files in the codebase and the production-hardened Docker setup — non-root container (UID 1001), healthchecks, Docker Secrets support, named volume persistence — mean you're not sacrificing reliability for sovereignty.
Quick Start Recap
- Clone the repo:
git clone https://github.com/waiaas/WAIaaS.git - Start the daemon:
docker compose up -d - Create a wallet using masterAuth via the REST API
- Create a session token for your agent
- Set policies to constrain what your agent can do
- Connect via MCP or SDK and point at
http://127.0.0.1:3100
What's Next
The best next step is exploring the policy engine in depth — especially the DeFi-specific policy types if you're building an agent that interacts with lending protocols or perpetual futures. After that, look at the MCP multi-wallet configuration for running separate agents with separate wallets and separate permission scopes on the same daemon instance.
Star the project on GitHub and follow the development at https://github.com/waiaas/WAIaaS, or visit https://waiaas.ai to learn more. Your keys, your server, your rules.
Top comments (0)