Where should your AI agent's private keys live? This single decision determines your security model, regulatory exposure, liability structure, and operational complexity.
Get it wrong, and you either lose control of your funds or become responsible for custodying millions in customer assets.
The Two Models
Custodial: You Hold the Keys
In a custodial model, a service provider holds private keys on behalf of your agents.
┌─────────────────────────────────────────────────────┐
│ Custodial Provider │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Agent A │ │ Agent B │ │ Agent C │ │
│ │ Keys │ │ Keys │ │ Keys │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ [HSM] [Insurance] [Compliance] [Audit] │
└─────────────────────────────────────────────────────┘
▲
│ API calls
│
┌─────────────────────────────────────────────────────┐
│ Your Application │
│ Agent A Agent B Agent C │
│ (no keys) (no keys) (no keys) │
└─────────────────────────────────────────────────────┘
Examples: Fireblocks, Anchorage, BitGo, Copper
Non-Custodial: Agents Hold Their Own Keys
In a non-custodial model, your infrastructure controls the private keys directly.
┌─────────────────────────────────────────────────────┐
│ Your Infrastructure │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐│
│ │ Agent A │ │ Agent B │ │ Agent C ││
│ │ + Keys │ │ + Keys │ │ + Keys ││
│ │ + Policy │ │ + Policy │ │ + Policy ││
│ └─────────────┘ └─────────────┘ └─────────────┘│
│ │
│ [Your HSM/KMS] [Your Controls] [Your Audit] │
└─────────────────────────────────────────────────────┘
Examples: Self-hosted wallets with PolicyLayer, MPC solutions you operate
The Trade-offs
| Factor | Custodial | Non-Custodial |
|---|---|---|
| Key security | Provider's responsibility | Your responsibility |
| Regulatory burden | Provider handles licensing | May need your own licenses |
| Insurance | Usually included | You arrange separately |
| Latency | API round-trip (50-200ms) | Local signing (<10ms) |
| Control | Limited to provider's features | Full control |
| Vendor lock-in | High (keys with provider) | Low (your keys) |
| Cost | Basis points on AUM | Fixed infrastructure cost |
| Liability | Shared with provider | Entirely yours |
When to Choose Custodial
You Should Use Custodial If:
1. You're holding customer funds
If your agents manage funds that belong to your users (not your company), custodial providers offer:
- Regulatory compliance (MSB, MTA licenses)
- Insurance against theft
- Professional-grade security
- Audit trails for regulators
// Customer funds → Custodial provider
const customerWallet = new CustodialProvider({
apiKey: process.env.FIREBLOCKS_API_KEY,
vaultId: 'customer-funds-vault'
});
// Agent requests withdrawal on behalf of customer
await customerWallet.createTransaction({
amount: withdrawalAmount,
destination: customerAddress,
// Provider enforces their own limits
});
2. You lack crypto security expertise
Key management is hard. HSMs, key ceremonies, backup procedures, rotation policies—one mistake and funds are gone forever.
Custodial providers employ teams of security specialists. Unless you can match that, outsource it.
3. You need institutional-grade insurance
Most custodians offer $100M+ insurance policies. Getting equivalent coverage independently is expensive or impossible.
4. Regulatory clarity is essential
If you're a regulated entity (bank, broker, fund), using a licensed custodian simplifies compliance. The custodian handles the crypto-specific regulations.
When to Choose Non-Custodial
You Should Use Non-Custodial If:
1. You're managing your own company's funds
If agents are spending your treasury (not customer funds), you don't need a licensed custodian. You're just managing your own money.
// Company funds → Self-custody with PolicyLayer
const companyWallet = new PolicyWallet(
await createEthersAdapter(companyPrivateKey, rpcUrl),
{
apiKey: process.env.POLICYLAYER_API_KEY,
metadata: {
orgId: 'acme-corp',
walletId: 'operations-treasury'
}
}
);
// Agent operates with policy-enforced limits
await companyWallet.send({
chain: 'ethereum',
asset: 'usdc',
to: vendorAddress,
amount: paymentAmount
});
2. Latency matters
Custodial APIs add 50-200ms per transaction. For high-frequency trading or time-sensitive operations, that's too slow.
Non-custodial signing happens locally in under 10ms.
3. You need full control
Custodial providers decide:
- Which chains they support
- Which tokens they allow
- What limits they impose
- When they're available (maintenance windows)
Non-custodial means you control every aspect.
4. Cost scales with volume
Custodial pricing is typically basis points on AUM or per-transaction fees. At high volumes, this gets expensive.
Non-custodial has fixed infrastructure costs regardless of volume.
The PolicyLayer Approach: Non-Custodial + Controls
PolicyLayer is designed for the non-custodial model. You keep your keys; we provide the policy enforcement layer.
┌─────────────────────────────────────────────────────┐
│ Your Infrastructure │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ PolicyLayer │ │
│ │ ┌────────────────────────────────────┐ │ │
│ │ │ Gate 1: Intent Validation │ │ │
│ │ │ - Spending limits │ │ │
│ │ │ - Recipient whitelist │ │ │
│ │ │ - Transaction frequency │ │ │
│ │ └────────────────────────────────────┘ │ │
│ │ ┌────────────────────────────────────┐ │ │
│ │ │ Gate 2: Execution Verification │ │ │
│ │ │ - Fingerprint match │ │ │
│ │ │ - Single-use token │ │ │
│ │ └────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────┘ │
│ ▲ │
│ │ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐│
│ │ Agent A │ │ Agent B │ │ Agent C ││
│ │ + Keys │ │ + Keys │ │ + Keys ││
│ └─────────────┘ └─────────────┘ └─────────────┘│
│ │
│ [Your KMS] │
└─────────────────────────────────────────────────────┘
What this gives you:
- Keys never leave your infrastructure
- Hard spending limits enforced before signing
- Full audit trail of all transactions
- Kill switch for emergencies
- No basis-point fees on volume
What this doesn't give you:
- Insurance (arrange separately)
- Regulatory licensing (only needed for customer funds)
- Key management (you handle this)
Hybrid Approach: When to Use Both
Some organisations use both models:
┌─────────────────────────────────────────────────────┐
│ Customer Funds │
│ (Custodial - Fireblocks) │
│ │
│ Withdrawals │ Deposits │ Cold Storage │
└─────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ Company Operations │
│ (Non-Custodial + PolicyLayer) │
│ │
│ Trading │ DeFi │ Payments │ Refunds │
└─────────────────────────────────────────────────────┘
Use custodial for:
- Customer funds (regulatory requirement)
- Long-term cold storage
- Large institutional holdings
Use non-custodial for:
- Operational treasury
- High-frequency trading
- DeFi interactions
- Internal payments
Implementation: Non-Custodial with PolicyLayer
Step 1: Set Up Key Management
Store private keys securely using a KMS:
import { KMSClient, DecryptCommand } from '@aws-sdk/client-kms';
async function getPrivateKey(): Promise<string> {
const kms = new KMSClient({ region: 'us-east-1' });
const response = await kms.send(new DecryptCommand({
CiphertextBlob: Buffer.from(process.env.ENCRYPTED_PRIVATE_KEY, 'base64'),
KeyId: process.env.KMS_KEY_ID
}));
return Buffer.from(response.Plaintext!).toString('utf-8');
}
Step 2: Create Policy-Wrapped Wallet
import { PolicyWallet, createEthersAdapter } from '@policylayer/sdk';
const privateKey = await getPrivateKey();
const adapter = await createEthersAdapter(privateKey, rpcUrl);
const wallet = new PolicyWallet(adapter, {
apiKey: process.env.POLICYLAYER_API_KEY,
metadata: {
orgId: 'acme-corp',
walletId: 'trading-hot',
agentId: 'arbitrage-bot'
}
});
Step 3: Configure Policy Limits
{
"agentId": "arbitrage-bot",
"limits": {
"perTransactionLimit": "10000000000",
"dailyLimit": "100000000000",
"hourlyLimit": "25000000000",
"maxTxPerHour": 100
},
"recipientWhitelist": [
"0x...dex-router-1",
"0x...dex-router-2"
]
}
Step 4: Execute with Controls
// Every transaction goes through policy checks
const result = await wallet.send({
chain: 'ethereum',
asset: 'usdc',
to: dexRouter,
amount: tradeAmount
});
// PolicyLayer enforces:
// ✓ Amount within per-transaction limit
// ✓ Daily spend within budget
// ✓ Recipient in whitelist
// ✓ Transaction frequency acceptable
Security Comparison
Custodial Security Model
| Attack Vector | Protection |
|---|---|
| Key theft | HSMs, MPC, insurance |
| Insider threat | Background checks, separation of duties |
| API compromise | Rate limits, IP whitelisting, 2FA |
| Provider hack | Insurance, distributed infrastructure |
Non-Custodial + PolicyLayer Security Model
| Attack Vector | Protection |
|---|---|
| Key theft | Your KMS/HSM, your security practices |
| Agent compromise | Policy limits cap damage |
| Prompt injection | Deterministic policy blocks bad transactions |
| Replay attacks | Single-use tokens, fingerprinting |
Key insight: Non-custodial doesn't mean "no controls." It means you implement the controls yourself—and PolicyLayer provides the enforcement layer.
Decision Framework
Do your agents handle customer funds?
├── YES → Use custodial provider
│ (Regulatory requirement, insurance needed)
│
└── NO → Agents manage company treasury
│
Is latency critical (under 50ms)?
├── YES → Non-custodial + PolicyLayer
│
└── NO → Do you have crypto security expertise?
├── YES → Non-custodial + PolicyLayer
└── NO → Consider custodial for simplicity
Related Reading
- Treasury Management for AI Agents — Structuring multi-agent wallets
- Two-Gate Enforcement — How non-custodial controls work
- The Kill Switch — Emergency controls for non-custodial setups
Ready to add controls to your non-custodial agents?
- Quick Start Guide — Production-ready in 5 minutes
- GitHub — Open source SDK
Top comments (0)