AI proposes disaster relief, the chain certifies, donors verify — an on-chain relief fund on Base.
CivicShield is an on-chain disaster-relief fund where AI can propose, but only the chain can release money — "generation is not permission." Anyone can donate any token from any chain in one click. A multi-agent system monitors real-world hazard data and drafts fund releases, but the agents hold no keys: their only on-chain power is to enqueue a proposal. Each proposal is certified by an escrow + policy contract on Base mainnet against six deterministic rules, checked in order with the first failure winning — the event's region|hazard matches the pool's scope (donor intent), riskScore ≥ 75, amount within the per-event cap, within a trace-level daily limit, recipient on a verified allowlist, and purpose approved. Crucially, the release condition is a Chainlink-attested riskScore from a live federal weather feed, not the AI's say-so, so a manipulated agent can at worst miss a disaster — never cause a wrongful release. Execution is permissionless: a keeper settles any clean proposal automatically. But a policy-clean release at or above the review threshold doesn't auto-pay — it's frozen as PENDING_REVIEW until a human signs on a Ledger hardware wallet. Every outcome, release or block, is logged on-chain (ActionEvaluated); the chain itself is the audit trail, no database. The result: a verified hazard signal — not a committee, not an unaccountable AI — is what unlocks relief, and donors can verify every dollar.
Contract (contracts/ — Solidity + Foundry, deployed on Base mainnet, 19 tests). CivicShieldPool is an escrow + "permissibility machine." proposeRelease is gated to an onlyAgent role; executeRelease is permissionless — the policy guards the money, not the caller — and runs six deterministic rules in order, reporting the first failure. It never reverts on a policy failure: blocks are recorded on-chain via ActionEvaluated, so attacks become public history, and there's no admin-drain path. Releases ≥ reviewThreshold flip to PENDING_REVIEW and wait for approveRelease.
Hazard oracle — Chainlink CRE. A TypeScript CRE workflow pulls live NWS alerts from api.weather.gov/alerts/active and maps the CAP fields severity/urgency/certainty into a deterministic 0–100 riskScore (fixed weights, no LLM in the consensus path); a viem relayer submits it on-chain with the event's attested scope via submitRiskScore. The score is the release condition, not decoration.
Proposer — Privy + OpenAI. A cheap supervisor agent watches the scope; only on a real anomaly does it spawn an OpenAI assessor that judges severity and drafts a structured proposal clamped to policy limits. It signs proposeRelease with a Privy Agent Wallet (server wallet, custodied by Privy) — that wallet is the contract's onlyAgent principal — so it runs unattended on a GitHub Actions cron with no raw key anywhere.
Settler — keeper. agent/keeper.ts is a cron/--watch daemon that calls the permissionless executeRelease: it settles fresh PENDING proposals instantly and settles a PENDING_REVIEW the moment it sees the Ledger's ReleaseApproved event. No trusted operator, no manual poking.
Human gate — Ledger. For large releases a Ledger hardware wallet is the approver. We drive it with the Device Management Kit + device-signer-kit-ethereum (connect → review → sign approveRelease → broadcast; the key never leaves the device) and ship a Clear Signing (ERC-7730) descriptor so the device shows "Approve disaster-relief release · Proposal #N" instead of raw hex. Proven end-to-end on Base mainnet through the Speculos emulator: proposed → PENDING_REVIEW → device-signed approveRelease (ReleaseApproved) → keeper auto-settled (EXECUTED).
Donation intake — LI.FI Composer. Donors pay any token on any chain; Composer swaps + deposits into the pool as one atomic Flow with one signature, using the escrow contract as an arbitrary on-chain destination. We call the contractCalls quote endpoint directly and pass LI.FI's legacy transaction through to the wallet untouched.
Frontend. Next.js 16 (App Router) + React 19 + wagmi v3 + viem + Tailwind v4, reading all state straight from getProposal and on-chain events through a single committed deployment artifact (address/ABI never hand-copied).
Notable/hacky. The donation path failed under MetaMask's broadcast backend — we traced it to the wallet's RPC, not our code, switched to Rabby, and landed a real USDC donation on mainnet. We ran the full Ledger approval through the Speculos emulator (swap one transport line for physical hardware). And the chain is the only trace store — ActionEvaluated events back a live transparency log, so "donors verify" is literally clickable.

