KAI is a financial permission layer for autonomous AI agents, built for the Aleph WDK Track 1 hackathon. It gives an agent a testnet USD₮ wallet, deterministic spending controls, an auditable transaction trail, and the ability to discover and pay for x402-protected APIs.
Public repository: github.com/Klashka-Group/Aleph-hackathon.
KAI combines:
- a WDK-backed EVM wallet initialized from a dedicated test seed;
- USD₮ balance retrieval, transfers, confirmation polling, and transaction history;
- a domain- and recipient-aware policy engine enforced before every transfer, with a full human-approval workflow (pending → approve/reject → on-chain execution) for payments above the approval threshold;
- x402 HTTP 402 discovery, requirement parsing, payment verification, one-time request replay, and audit logging;
- a Claude-driven autonomous agent loop wired to all of the above — the model calls tools that hit the real wallet, policy, and x402 modules, and transparently retries a paid x402 request without ever tracking a requirement ID or transaction hash itself;
- demo endpoints for a small approved payment ($0.03) and a large policy-rejected payment ($0.80);
- a Next.js dashboard and Supabase metadata/audit storage.
Wallet keys never go into Supabase. WDK owns the seed in the server process, while Supabase stores agents, tasks, policies, events, approvals, and transaction metadata.
Status: M0 (Foundation), M1 (WDK Wallet), M2 (Policy Engine & Guardrails), M3 (Agent Runtime), M4 (x402 & Payments), M5 (Dashboard), and M6 (End-to-End Integration) are complete. M7 (Demo & Submission) is in progress.
Project context and acceptance criteria are in INFO/CONTEXT.md, INFO/CHECKLIST.md, and INFO/GITHUB_ISSUES.md.
- Safe autonomy at the money boundary. An LLM is probabilistic, while a payment decision must be deterministic. KAI keeps policy enforcement outside the model: recipient and domain rules, per-payment limits, daily budgets, balances, and approval thresholds are re-evaluated immediately before funds can move.
- A complete x402 payment state machine. The agent must recognize HTTP 402, validate and persist the payment requirement, pay the exact token/recipient/ amount/network, verify confirmation, and replay the original request once without allowing a proof to be reused.
- Reliable on-chain execution through WDK. The implementation manages seed isolation, wallet lifecycle, six-decimal USD₮ conversion, RPC failures, balance caching, transaction broadcasting, confirmation polling, and durable audit updates without exposing keys to the browser or database.
- Concurrency and hostile-input safety. A wallet-wide lock prevents nonce and balance races. Internal authentication, same-origin checks, rate limits, strict request validation, SSRF defenses, and fail-closed errors protect the agent's financial tools.
- Human control without breaking autonomy. Payments above the configured threshold enter an immutable approval workflow; an approval re-checks the remaining budget before on-chain execution and cannot be double-executed.
KAI connects a Claude tool-calling loop, deterministic policy engine, Tether WDK wallet, EVM transaction lifecycle, x402 protocol adapter, Supabase audit model, secured Next.js route handlers, and a supervision dashboard into one bounded workflow. The difficult parts are handled explicitly: decimal-safe amounts, idempotent approvals and retries, transaction finality, concurrent wallet access, partial failures, secret boundaries, and auditable state transitions.
The core product flow is implemented: an agent can encounter an x402 resource, evaluate policy, make a WDK-powered USD₮ payment or request human approval, verify it, retry the resource once, and expose the result and audit trail in the dashboard. M0–M5 are complete; M6 and M7 cover final end-to-end hardening and submission work. The remaining plan is concrete: run the full flow against the live configured services, finish automated integration coverage, validate the clean deployment environment, record the demo, and complete submission polish.
Autonomous agents usually face an unsafe choice: they either cannot purchase resources without a person in every loop, or they receive wallet access with insufficient controls. KAI introduces a programmable financial permission layer between agent intent and wallet execution. It combines x402's machine- native paywall with WDK self-custody, deterministic budgets, selective human approval, and evidence-grade auditability. The model can decide what tool it needs, but it cannot decide to bypass the rules governing whether and how money moves.
The design uses deployable components—Next.js, Supabase, WDK, EVM JSON-RPC, USD₮, and standard HTTP—so it can be integrated into existing agent products without a custom blockchain. Real-world uses include paying per request for market data, research, inference, storage, compute, licensed content, or other machine services while enforcing team budgets and recipient restrictions. Moving beyond the hackathon MVP mainly requires operational upgrades rather than a redesign: managed secret custody, durable background jobs, shared rate limiting/locks, production monitoring, and the target network's deployment and compliance controls.
These links are pinned to immutable commit hashes, so their line numbers do
not move even as main continues to change.
Pinned to dc1eb83 (M0/M1/M4 — foundation, WDK wallet, x402):
Pinned to f5717ff (M2 — policy engine, guardrails, approval workflow):
| Integration | Direct permalink |
|---|---|
| Domain- and recipient-aware policy evaluation | lib/payment-policy.ts lines 1–61 |
| Guardrails CRUD + daily-spend tracker | lib/guardrails.ts lines 1–207 |
| REQUIRES_APPROVAL creates a pending approval, not a rejection | lib/wallet.ts lines 301–329 |
| Post-approval on-chain execution | lib/wallet.ts lines 351–387 |
| Approve/reject workflow (double-click-safe) | lib/approvals.ts lines 1–213 |
Pinned to 85fde8d (M3 — agent runtime):
| Integration | Direct permalink |
|---|---|
| Agent task loop (runAgentTask) | lib/agent.ts lines 1–188 |
| Transparent x402 pay-and-retry tool handler | lib/agent-tools-handlers.ts lines 243–301 |
Exact versions are taken from package-lock.json.
| Package | Version | How KAI uses it |
|---|---|---|
| @tetherto/wdk | 1.0.0-beta.16 | Core in-process SDK: seed validation, wallet registration, accounts, transfers, balances, and confirmations |
| @tetherto/wdk-wallet-evm | 1.0.0-beta.11 | EVM wallet manager registered with WDK for Ethereum Sepolia |
| @tetherto/wdk-cli | 1.0.0-beta.3 | Creates, exports, unlocks, inspects, and funds the demo wallet; also bundles optional wdk-mcp tooling |
See the declarations in package.json lines 21–23.
| Setting | Demo value |
|---|---|
| Chain | Ethereum Sepolia testnet |
| Chain ID | 11155111 |
| WDK/CLI network | sepolia |
| x402 CAIP-2 network | eip155:11155111 |
| Gas token | Sepolia ETH |
| Payment token | USD₮ / USDT, ERC-20, 6 decimals |
| USD₮ contract | 0xd077A400968890Eacc75cdc901F0356c943e4fDb |
| Default RPC | https://ethereum-sepolia-rpc.publicnode.com |
We did not deploy a mock USD₮. The demo uses the Sepolia USDT registration exposed by WDK CLI. Verify it with:
npx wdk token info --network sepolia --token usdt --jsonsepolia.drpc.org (a common default in other WDK examples) rejects Sepolia
RPC calls on its free tier ("chain is not available on free plan") — verified
directly against the live endpoint. publicnode's free public RPC works; swap
providers if it ever rate-limits you.
This build uses a standard EOA through WalletManagerEvm and direct JSON-RPC. It does not use ERC-4337:
- BUNDLER_URL: not used;
- PAYMASTER_URL: not used;
- bundler/paymaster contract addresses: not applicable.
- Git
- Node.js 22.18.0 or newer
- npm
- A Supabase project
- An Anthropic API key
- Sepolia ETH and Sepolia USD₮ for the test wallet
Never use a seed phrase connected to real funds.
git clone https://github.com/Klashka-Group/Aleph-hackathon.git
cd Aleph-hackathon
npm cimacOS/Linux:
cp .env.example .env.localWindows PowerShell:
Copy-Item .env.example .env.localRequired configuration:
| Variable | Required | Purpose |
|---|---|---|
| SUPABASE_URL | Yes | Supabase project URL |
| SUPABASE_SECRET_KEY | Yes | Server-only Supabase secret/service-role credential |
| ANTHROPIC_API_KEY | Yes | Claude API access (agent runtime) |
| WDK_WALLET_SEED | Yes | Dedicated test-only BIP-39 seed used by WDK |
| WALLET_ADDRESS | Yes | Address displayed and funded for the demo |
| NETWORK | Yes | sepolia |
| RPC_URL | Yes | Ethereum Sepolia JSON-RPC endpoint |
| USDT_CONTRACT_ADDRESS | Yes | Sepolia USD₮ contract shown above |
| USDT_DECIMALS | Yes | 6 |
| INTERNAL_API_KEY | Yes | Server key sent as x-agentpay-internal-key to every private route (wallet, guardrails, approvals, agent, x402) |
| X402_PAYMENT_RECIPIENT | x402 demo | Testnet address receiving demo payments |
| X402_NETWORK | x402 demo | eip155:11155111 |
| X402_ALLOW_PRIVATE_HOSTS | Optional | Set true only for local testing against non-public hosts; disables the x402 tool's SSRF guard |
| DEMO_MODE | Optional | Enables explicit local mock-payment confirmation when true |
| WDK_INDEXER_BASE_URL | Optional | Only for WDK CLI/indexer history |
| WDK_INDEXER_API_KEY | Optional | Indexer credential |
Legacy Supabase names NEXT_PUBLIC_SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are also supported. Keep secrets only in the git-ignored .env.local file.
npm run wallet:create
npm run wallet:unlock
npx wdk wallet export --name agentpay-test
npm run wallet:addressCopy the exported seed into WDK_WALLET_SEED. Fund the reported address with Sepolia ETH for gas and Sepolia USD₮, then verify:
npx wdk get balance --network sepolia --wallet agentpay-test --json
npx wdk get balance --network sepolia --token usdt --wallet agentpay-test --jsonSee wallet-config/README.md for the complete, testnet-only wallet walkthrough.
Run these files in the Supabase SQL editor, in order:
- supabase/schema.sql
- supabase/migrations/20260822203000_x402_support.sql
- Optional demo data: supabase/seed.sql
npm run devOpen http://localhost:3000. Check the foundation at http://localhost:3000/api/health.
npm test
npm run typecheck
npm run lint
npm run buildKAi can simulate Spot MARKET BUY and SELL operations using a dedicated Binance
Spot Testnet account. It never accepts Binance Mainnet endpoints, withdrawals,
margin, futures, or leverage. Configure the demo-only credentials in the
git-ignored .env.local; never commit them. The full key setup, demo prompts,
audit events, and x402/WDK separation are documented in
docs/binance-testnet-demo.md.
All routes below are private server-side tools and require the
x-agentpay-internal-key header. The deliberate exception is
GET /api/paid-data: it represents the external merchant API that returns
HTTP 402, so the agent's internal credential must not unlock it.
GET /api/wallet?agentId=<uuid> # wallet metadata + cached USD₮ balance
GET /api/wallet/transactions?agentId=<uuid> # latest 50 audited transfers
POST /api/wallet/transfer # policy-checked USD₮ transfer
GET /api/guardrails?agentId=<uuid> # current guardrails + today's spending
PATCH /api/guardrails?agentId=<uuid> # partial guardrail update
GET /api/approvals?agentId=&status= # list approvals (default status=PENDING)
POST /api/approvals/[id] # approve/reject and resume the paused task
POST /api/approvals/[id]/approve # backward-compatible approval alias
POST /api/approvals/[id]/reject # backward-compatible rejection alias
POST /api/agent/task # create + synchronously run a task to completion/pause/failure
GET /api/agent/status?agentId=<uuid> # agent state, latest task, pending approvals, recent activity
GET /api/events?agentId=<uuid>&after=<ISO> # recent events or incremental polling updates
POST /api/x402-request # discovers and stores an upstream 402
POST /api/x402-retry # verifies payment and replays once
POST /api/x402-demo # resets/runs the demonstration scenario
POST /api/paid-data/reset # resets the simulated merchant
GET /api/paid-data # public simulated paid merchant API
Example wallet calls:
curl "http://localhost:3000/api/wallet?agentId=AGENT_UUID" \
-H "x-agentpay-internal-key: YOUR_INTERNAL_API_KEY"
curl -X POST "http://localhost:3000/api/wallet/transfer" \
-H "content-type: application/json" \
-H "x-agentpay-internal-key: YOUR_INTERNAL_API_KEY" \
-d '{"agentId":"AGENT_UUID","recipient":"0x...","amount":"0.03","type":"x402_payment"}'Example task call:
curl -X POST "http://localhost:3000/api/agent/task" \
-H "content-type: application/json" \
-H "x-agentpay-internal-key: YOUR_INTERNAL_API_KEY" \
-d '{"agentId":"AGENT_UUID","title":"Research ETH market activity. Your task budget is $0.50."}'lib/agent.ts's runAgentTask(taskId) loads the task + agent, builds the
system prompt (lib/system-prompt.ts) with the agent's real daily budget and
approval threshold, then calls Claude with the 6 tools from
lib/agent-tools.ts in a loop (50-iteration cap), dispatching each tool call
via lib/agent-tools-handlers.ts to the real wallet/policy/x402 modules.
check_policyre-evaluates the domain-aware policy engine and logspolicy_evaluation_started/policy_approved/policy_rejected/policy_requires_approvalevents.send_paymentcallssendUsdtPayment— onAWAITING_APPROVALit does not fail the task; it stops the loop cleanly (the agent's status was already flipped bylib/wallet.ts) and returns{outcome:"AWAITING_APPROVAL", ...}. The loop persists its messages and pending x402 requirements in the task's JSON checkpoint.POST /api/approvals/[id]records the immutable human decision, executes an approved transfer, restores that checkpoint, and resumes the same task without submitting the payment a second time.make_http_requesttransparently retries a paid x402 request: it tracks pending unpaid requirements per URL in-memory for the task's lifetime. A matchingsend_paymentis tagged asx402_payment, carries the paid API's domain into the second policy check, and stores its confirmed hash on that pending requirement. When the model calls the same URL again, the handler verifies and replays the original request vialib/x402/service.ts'sretryX402Paymentautomatically. The model never sees a requirement ID or transaction hash; it just callsmake_http_requestagain, exactly aslib/system-prompt.tsinstructs.
Any unexpected failure anywhere in the loop (a Claude API error the bounded
retry couldn't recover, a tool-glue bug, etc.) is caught by one outer
try/catch that marks the task FAILED, the agent ERROR, and logs
task_failed — a task can never end up stuck RUNNING with no audit trail.
npm test includes deterministic integration coverage for the complete
402 → policy approval → payment → verified replay → data sequence, including
the event order consumed by the dashboard. It also exercises the real x402
parser/store/replay service against a simulated HTTP merchant boundary.
Run the live smoke test against a configured local or deployed instance to
exercise the real LLM, Supabase, WDK test wallet, paid endpoint, and dashboard
status API. The command fails if the task does not complete, any required
activity event is missing or out of order, or the flow takes 60 seconds or
longer. It performs a WDK balance preflight and requires at least 0.03
USD₮ plus enough Sepolia ETH for gas:
AGENTPAY_BASE_URL=http://localhost:3000 \
AGENTPAY_AGENT_ID=YOUR_AGENT_UUID \
INTERNAL_API_KEY=YOUR_INTERNAL_API_KEY \
npx tsx scripts/x402-live-smoke.mtsapp/dashboard/* (Overview, Tasks, Wallet, Guardrails, Activity, Approvals)
lets a non-technical user run and supervise the agent without touching the
API directly or understanding crypto. Visit /dashboard after npm run dev.
Architecture note: the dashboard never calls the INTERNAL_API_KEY-gated
API routes from the browser — doing so would require embedding that
server-only secret in client JS. Instead:
- All reads are React Server Components that import
lib/wallet.ts,lib/guardrails.ts,lib/approvals.ts, andlib/dashboard-data.tsdirectly, in-process, with zero HTTP round-trip and zero exposed secrets. - Browser-started tasks use the session-cookie-gated
POST /api/agent/task/streamroute, which validates wallet ownership and streams NDJSON deltas to the chat without exposingINTERNAL_API_KEY. - Approving/rejecting a payment, updating guardrails, creating an agent, and
saving encrypted per-agent LLM settings use authenticated Server Actions
(
lib/dashboard-actions.ts,"use server"). - "Real-time" updates (#30) are a
router.refresh()poll every 4s (components/dashboard/auto-refresh.tsx), re-running the Server Components for the current route without a full reload. Chosen over Supabase Realtime/SSE for simplicity — the tables have zero anon-role RLS policies by design (service-role only), and standing up a second real-time channel wasn't worth it for a few seconds of latency nobody notices in a demo. Agent text itself arrives immediately through the streaming task response. - Task execution is still request-bound; there is no durable background-job queue in this MVP, even though the chat renders progress incrementally.
The existing internal-key-gated API routes are untouched and still serve their original purpose: the agent runtime's own tool calls, x402 demo endpoints, and any external tooling.
- Use a dedicated wallet with testnet funds only. Never commit .env.local, seed phrases, private keys, Supabase secrets, or internal API keys.
- WDK key material stays in server memory and is explicitly disposed through the wrapper; it is never persisted in Supabase.
- Server-to-server financial, x402, guardrail, approval, and agent routes fail closed with a constant-time internal-key check. Browser-facing auth/task routes use signed wallet sessions plus ownership and same-origin checks. Sensitive routes limit JSON body size, reject unknown fields, and return no-store responses.
- Transfers validate identifiers and monetary strings, then re-read recipient restrictions, policy limits (including domain allow/block lists), daily spending, and balance immediately before WDK is invoked. A missing or ambiguous policy denies payment.
- A human's approval decision is immutable and distinct from execution
outcome: rejecting a payment never touches the chain; approving one
re-checks the daily budget (not recipient/domain/per-tx — those were the
human's call) before broadcasting, and
decideApprovalis double-click-safe (an already-decided approval returns 409, not a second execution). - A process-wide wallet lock serializes transfers because all agent records share one physical WDK account, preventing concurrent nonce/balance races.
- The x402 HTTP tool resolves target DNS and blocks credentials plus localhost, private, link-local, and multicast destinations to mitigate SSRF.
- x402 retries bind a proof to the expected amount, token, recipient, and network; each confirmed payment can unlock only one replay.
- Rate limiting is in-process for this hackathon runtime. A multi-instance production deployment should use a shared edge or datastore limiter.
WDK references: Core SDK documentation, EVM wallet documentation, and the WDK CLI guide.
Built for the Tether WDK Track 1 hackathon.
