DEV Community

Cover image for Production Docker for Monorepo AI Wallets: 15-Package Architecture Deployment Guide
Wallet Guy
Wallet Guy

Posted on

Production Docker for Monorepo AI Wallets: 15-Package Architecture Deployment Guide

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

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

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

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

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

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

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

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..."
Enter fullscreen mode Exit fullscreen mode

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

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

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

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

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}`);
Enter fullscreen mode Exit fullscreen mode

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

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

  1. Clone the repo: git clone https://github.com/waiaas/WAIaaS.git
  2. Start the daemon: docker compose up -d
  3. Create a wallet using masterAuth via the REST API
  4. Create a session token for your agent
  5. Set policies to constrain what your agent can do
  6. 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)