Dev.to Security 🔐 Cybersecurity 👁 0 📖 8 min read

SPENDING_LIMIT + TIME_RESTRICTION: Create Trading Hours for Your AI Agent

SPENDING_LIMIT + TIME_RESTRICTION: Create Trading Hours for Your AI Agent Giving an AI agent a wallet without guardrails is like giving a toddler a credit card — and if you're building trading bots or autonomous agents

SPENDING_LIMIT + TIME_RESTRICTION: Create Trading Hours for Your AI Agent

Giving an AI agent a wallet without guardrails is like giving a toddler a credit card — and if you're building trading bots or autonomous agents that control real funds, that risk is not hypothetical. WAIaaS is an open-source, self-hosted Wallet-as-a-Service that lets you define exactly when your agent can transact, how much it can move, and what happens when it tries to exceed those bounds.

The Real Risk: Autonomous Agents and Unconstrained Access

Most wallet integrations for AI agents are binary: the agent either has access to funds or it doesn't. There's no middle ground — no "only during business hours," no "pause if the amount looks unusual," no "require my signature above $500." You hand over a private key, the agent does what it wants, and you find out about problems in your transaction history.

This is a meaningful risk. AI agents can behave unexpectedly. APIs they call can return bad data. Prompt injection attacks are real. A bug in your agent's logic at 3am, when you're asleep and gas is cheap, can move a lot of money very quickly.

The question isn't whether to give your agent wallet access. It's how to give it bounded access — access that can't exceed what you've explicitly allowed, at hours you've explicitly defined, for amounts you've explicitly approved.

How WAIaaS Approaches This

WAIaaS is built around a default-deny policy engine. Transactions are blocked unless you've explicitly configured what's allowed. There are 21 policy types that cover spending, timing, token whitelisting, contract whitelisting, network restrictions, DeFi-specific limits, and more.

Underneath that, there's a 3-layer security model:

  • Layer 1: Session authentication — your agent authenticates with a session token (JWT HS256), not a private key
  • Layer 2: The policy engine — every transaction passes through a 7-stage pipeline including policy evaluation before anything gets signed or broadcast
  • Layer 3: Monitoring and kill switch — the human owner can approve, delay, or cancel transactions via WalletConnect, Telegram, or push notification

For this post, we're focusing on two specific policy types that work well together: SPENDING_LIMIT and TIME_RESTRICTION. Combined, they let you define trading hours with tiered human oversight based on transaction size.

SPENDING_LIMIT: Four Tiers of Security Based on Amount

The SPENDING_LIMIT policy assigns every transaction to one of four security tiers based on its USD value:

  • INSTANT — Execute immediately, no notification
  • NOTIFY — Execute immediately, but send you a notification
  • DELAY — Queue the transaction for a configurable number of seconds before execution (cancellable during the window)
  • APPROVAL — Block execution entirely until you manually approve via WalletConnect, Telegram, or push notification

Here's how to create a SPENDING_LIMIT policy via the REST API:

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
    }
  }'

What this configuration does, exactly:

Transaction Amount Tier What Happens
$0 – $10 INSTANT Executes immediately
$10.01 – $100 NOTIFY Executes immediately, you get a notification
$100.01 – $1,000 DELAY Queued for 5 minutes, you can cancel
> $1,000 APPROVAL Blocked until you manually approve

And regardless of per-transaction tiers, the agent cannot exceed $500/day or $5,000/month in total. The daily and monthly caps are hard limits enforced by the policy engine — not something the agent can reason around.

This is the critical point: the agent doesn't decide its own limits. The policy is set by the master password holder (you, the system administrator) and enforced by the daemon before any signing happens. The agent's session token gives it the ability to initiate transactions, but the policy engine decides what actually executes.

TIME_RESTRICTION: Define When Your Agent Can Trade

The TIME_RESTRICTION policy lets you restrict transaction execution to specific hours of the day. Outside those hours, transactions are denied outright.

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": "TIME_RESTRICTION",
    "rules": {
      "allowedHours": {"start": 9, "end": 17},
      "timezone": "UTC"
    }
  }'

With this policy active, any transaction attempted outside 09:00–17:00 UTC will be denied. The agent gets back a structured error:

{
  "error": {
    "code": "POLICY_DENIED",
    "message": "Transaction denied by TIME_RESTRICTION policy",
    "domain": "POLICY",
    "retryable": false
  }
}

This is useful for several reasons:

  • You're asleep: Most people aren't watching their agents at 3am. If your strategy doesn't require 24/7 execution, restricting to hours when you can respond to APPROVAL-tier transactions matters.
  • Market conditions: Some traders only want activity during specific market sessions (US equities open, Asian session, etc.).
  • Operational hygiene: Restricting the attack window — the hours during which a compromised agent token could initiate transactions — is basic security practice.

A time restriction doesn't stop the agent from querying balances or positions. It specifically applies to transactions that would move funds.

Combining Both Policies

Policies in WAIaaS are additive and enforced in conjunction. A transaction must pass all active policies for a wallet. So if you have both SPENDING_LIMIT and TIME_RESTRICTION active, a $5 transaction attempted at 2am is blocked by TIME_RESTRICTION before it even reaches the spending tier logic.

Here's what the combined setup looks like in practice:

# Create 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
    }
  }'

# Create TIME_RESTRICTION 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": "TIME_RESTRICTION",
    "rules": {
      "allowedHours": {"start": 9, "end": 17},
      "timezone": "UTC"
    }
  }'

After this, your agent's effective permission set is:

  • Can only transact 09:00–17:00 UTC
  • Transactions under $10: immediate
  • Transactions $10–$100: immediate with notification
  • Transactions $100–$1,000: 5-minute delay, cancellable
  • Transactions over $1,000: blocked pending your approval
  • Hard cap: $500/day, $5,000/month

If your agent tries to exceed this — because of a bug, bad data, or anything else — the daemon blocks it. The agent doesn't have a way to override this; the session token it holds has no access to the policy API, which requires master password authentication.

Dry-Run Before You Commit

Before activating a policy configuration on a live wallet, you can simulate a transaction to verify it would be handled correctly:

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
  }'

With dryRun: true, the transaction goes through the full 7-stage pipeline — including policy evaluation — but stops before signing or broadcasting. You get back the tier it would be assigned to, whether it would be approved or denied, and why. This is useful for testing that your policy rules work as intended before your agent starts operating.

What About Token Whitelisting?

SPENDING_LIMIT and TIME_RESTRICTION control when and how much. But by default, WAIaaS also enforces default-deny on what tokens the agent can touch. If you don't configure an ALLOWED_TOKENS policy, the agent cannot transfer arbitrary tokens.

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": "ALLOWED_TOKENS",
    "rules": {
      "tokens": [
        {
          "address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
          "symbol": "USDC",
          "chain": "solana"
        }
      ]
    }
  }'

This is the complete picture of defense in depth: time restrictions gate when, spending limits gate how much, and token whitelists gate what. All three are enforced by the daemon, not by the agent.

Quick Start: Set Up Policies in 5 Steps

Here's the minimal path to a policy-protected agent:

Step 1: Install the CLI and start the daemon

npm install -g @waiaas/cli
waiaas init
waiaas start

Step 2: Create a wallet and session

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"}'

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>"}'

Step 3: Apply your SPENDING_LIMIT policy (as shown above)

Step 4: Apply your TIME_RESTRICTION policy (as shown above)

Step 5: Test with a dry-run before going live (as shown above)

From this point, your agent uses its session token for all operations. The session token grants access to transactions and balance queries — not to wallet creation, session management, or policy configuration. Those require the master password, which the agent never sees.

The Authentication Boundary

One thing worth being explicit about: WAIaaS uses three distinct auth methods for three distinct roles.

# masterAuth — system administrator (wallet creation, session management, policies)
-H "X-Master-Password: my-secret-password"

# sessionAuth — AI agent (transactions, balance queries, DeFi actions)
-H "Authorization: Bearer wai_sess_eyJhbGciOiJIUzI1NiJ9..."

# ownerAuth — fund owner (transaction approval, kill switch recovery)
-H "X-Owner-Signature: <ed25519-or-secp256k1-signature>"
-H "X-Owner-Message: <signed-message>"

The agent only ever holds a session token. It can't create new sessions. It can't modify policies. It can't approve its own transactions. The APPROVAL tier requires an owner signature — a cryptographic signature from the wallet's owner key — which the agent doesn't have and can't forge.

When a transaction hits the APPROVAL tier, the owner gets notified through one of three signing channels: push notification, Telegram, or WalletConnect. You review it and sign or reject. The daemon waits.

# Approve a pending transaction (ownerAuth)
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>"

This is the "human in the loop" mechanism that kicks in when transaction sizes exceed your comfort threshold for autonomous execution.

What's Next

The full policy engine covers 21 policy types — including DeFi-specific limits like PERP_MAX_LEVERAGE, LENDING_LTV_LIMIT, and VENUE_WHITELIST for trading applications that go beyond simple transfers. The interactive API reference at http://127.0.0.1:3100/reference documents all policy schemas with request/response examples once your daemon is running.

If you're serious about running agents with real funds, spending limits and time restrictions are the baseline — not the ceiling. Start there, verify your configuration with dry-runs, and layer in token whitelists and contract whitelists as your agent's scope becomes clearer.

The source code, deployment configs, and full documentation are available at:

WAIaaS is self-hosted and open-source. Your keys, your funds, your daemon — no third party in the signing path.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.