Agent wallet infrastructure with policy enforcement.
AI agents need to spend money. Giving them raw private keys is insane — no limits, no oversight, no kill switch. Steward sits between the agent and its wallet, enforcing policies you define.
Agent/Platform → Steward SDK → Policy Engine → Vault (AES-256-GCM) → Chain
↓
Approval Queue → Dashboard / Webhook
- Agent requests a transaction through the Steward SDK
- Policy engine evaluates the request — spending limits, approved addresses, rate limits, time windows, auto-approve thresholds
- All hard policies pass → signed and broadcast immediately
- Soft policy fails (auto-approve threshold) → queued for human approval
- Hard policy fails → rejected immediately, webhook fired
git clone https://github.com/0xSolace/steward
cd steward
cp .env.example .env # edit with your postgres URL + master password
docker compose up -dThe API is now running at http://localhost:3200.
npm install @stwd/sdk
# or: bun add @stwd/sdkimport { StewardClient } from '@stwd/sdk';
const steward = new StewardClient({
baseUrl: 'http://localhost:3200',
tenantId: 'my-platform',
apiKey: 'my-key',
});
// Create a wallet for an agent
const agent = await steward.createWallet('agent-1', 'Trading Bot');
console.log(agent.walletAddress); // 0x...
// Set policies
await steward.setPolicies(agent.id, [
{
id: 'limit',
type: 'spending-limit',
enabled: true,
config: {
maxPerTx: '100000000000000000', // 0.1 ETH
maxPerDay: '1000000000000000000', // 1 ETH
maxPerWeek: '5000000000000000000', // 5 ETH
},
},
{
id: 'addrs',
type: 'approved-addresses',
enabled: true,
config: { mode: 'whitelist', addresses: ['0xDEX...'] },
},
]);
// Sign a transaction — returns txHash or queues for approval
const result = await steward.signTransaction(agent.id, {
to: '0xDEX...',
value: '50000000000000000', // 0.05 ETH
chainId: 8453, // Base
});
if ('txHash' in result) {
console.log('Signed:', result.txHash);
} else {
console.log('Queued for approval:', result.status); // 'pending_approval'
}| Type | What it does |
|---|---|
spending-limit |
Cap per transaction, per day, per week (wei) |
approved-addresses |
Whitelist or blocklist destination addresses |
rate-limit |
Max transactions per hour / per day |
time-window |
Only allow transactions during defined UTC hours |
auto-approve-threshold |
Auto-sign below threshold; queue above for human review |
Policies are composable — mix and match. Hard policies (all except auto-approve-threshold) reject immediately on failure. The auto-approve threshold is the only soft gate; failure queues instead of rejects.
Configure a webhook URL on your tenant and Steward will POST on every state change:
| Event | When |
|---|---|
approval_required |
Transaction queued for manual review |
tx_signed |
Transaction signed and broadcast |
tx_rejected |
Transaction rejected by a hard policy |
tx_failed |
Transaction failed on-chain |
{
"type": "approval_required",
"tenantId": "my-platform",
"agentId": "agent-1",
"data": {
"txId": "...",
"to": "0x...",
"value": "50000000000000000",
"policyResults": [...]
},
"timestamp": "2024-01-01T00:00:00.000Z"
}- Dashboard: https://steward.fi/dashboard
- API: https://api.steward.fi
- On-chain proof: https://basescan.org/tx/0x8d7592b93cad0983b481451c6d0c05900a1c6d74ee7eadbcdc7533a77ae45dc0
This is a Bun monorepo managed with Turborepo.
| Package | Description |
|---|---|
@stwd/api |
Hono REST API — agents, policies, approvals, signing |
@stwd/vault |
AES-256-GCM encrypted keystore + transaction signing via viem |
@stwd/policy-engine |
Composable policy evaluation engine |
@stwd/sdk |
TypeScript HTTP client for agents and integrations (npm i @stwd/sdk) |
@stwd/db |
Drizzle ORM schema, migrations, and Postgres client |
@stwd/auth |
Timing-safe API key validation and tenant middleware |
@stwd/webhooks |
Webhook dispatch and retry queue |
@stwd/shared |
Shared types, interfaces, and constants |
web |
Next.js landing page and dashboard at steward.fi |
# Install all dependencies
bun install
# Copy env and fill in values
cp .env.example .env
# Start everything (API + Postgres via Docker, web in dev mode)
bun run devEnvironment variables:
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
✅ | PostgreSQL connection string |
STEWARD_MASTER_PASSWORD |
✅ | 256-bit hex secret for vault encryption |
PORT |
— | API port (default: 3200) |
RPC_URL |
— | EVM RPC endpoint (default: Base mainnet) |
CHAIN_ID |
— | Chain ID (default: 8453) |
STEWARD_DEFAULT_TENANT_KEY |
— | Dev API key for the default tenant |
The vault encrypts each agent's private key with AES-256-GCM using a key derived from the master password + agent ID. Keys never exist in plaintext outside of a signing operation. All signing happens in-process; the raw key is never sent over the wire.
Policy evaluation is stateless and synchronous. The engine receives the transaction request plus pre-fetched spend/rate context, evaluates all enabled rules, and returns an EvaluationResult with per-policy pass/fail details.