About the project
Offline Cash Aid
Cash aid that keeps flowing when the cell towers are down, and settles honestly the moment connectivity returns.
A CLI/TUI tool that lets field workers disburse stablecoin aid to disaster/crisis-affected beneficiaries entirely offline over a Bluetooth mesh, with real blockchain settlement happening automatically whenever any device in the network finds internet.
Aleph Hackathon 2026 — WDK Track (primary), Pears packaging (secondary).
---
The idea
Humanitarian cash assistance has already gone digital and on-chain at real scale (WFP Building Blocks $760M→~6M people; UNHCR on Stellar/USDC in Ukraine). But every one of these systems assumes connectivity at the moment of transaction. None solve the moment right after a disaster, when aid is needed most and towers, power, and internet are down.
This tool solves that specific gap: offline-signed, deferred-settlement cash disbursement — self-custodial (no organization holds or controls the funds or the ledger), using Bluetooth mesh for local transport and blockchain nonce semantics for safety.
Build phases
Each phase is an isolated proof, runnable individually:
| Phase | Script | Proof |
|---|---|---|
| 1. Offline WDK signing | scripts/phase1-sign.js | WDK signs an ERC-20/native tx with ZERO network calls (all fields populated explicitly) |
| 2. Nonce safety | scripts/phase2-nonce-conflict.js | two conflicting txs sharing a nonce: first settles, second auto-rejected; duplicate re-broadcast harmless |
| 3. Pending ledger | scripts/phase3-ledger.js | Hypercore append-only log replicates over encrypted streams; pending→confirmed derivation agrees |
| 4. Discovery | scripts/phase4-discovery.js | BLE attempted (falls back to a TCP mesh delivering the same NoiseSecretStream type); register/ACK protocol proven |
| 5. End-to-end offline | scripts/phase5-disburse.js | register→sign→log→replicate→pending balances, all with no RPC |
| 6. Sync & broadcast | scripts/phase6-broadcast.js | queued txs broadcast in nonce order when probe says online; ledgers converge |
| 7. Confirmation sync-back | scripts/phase7-confirm-propagate.js | the beneficiary device WITHOUT any rpc learns CONFIRMED purely from mesh |
| 10. Spending guardrails + batch | scripts/phase10-guardrails.js | caps refuse over-limit signing WITHOUT burning nonces; CSV batch signs good rows, refuses bad ones |
Live demo (all 7 phases in ~30s): npm run demo Unit tests (ledger folding + guardrail boundaries): npm test
Payment flow
- Register offline — field worker's device and beneficiary's WDK wallet discover each other over BLE mesh (
ble-swarm), zero internet required. - Coordinator signs — WDK account signs the transfer using a locally-reserved nonce. Signing is local; only broadcasting needs network.
- Both sides save — signed envelope written to append-only Hypercore on both devices. Pending balances update on both.
- Any device finds connectivity — replays queued signed txs in nonce order (WDK
sendTransaction/raw broadcast). - Chain confirms, mesh syncs back — confirmation propagates through Hypercore replication; pending → confirmed everywhere.
- Nonce conflicts auto-reject — two conflicting signed txs sharing a nonce: chain accepts first, rejects second; flagged for resend (proven in Phase 2).
Deploying on EVM or TON (account/nonce-based chains) is deliberate — the safety property holds cleanly there.
CLI reference
node src/cli.js help node src/cli.js init --seed <mnemonic> coordinator wallet (or) node src/cli.js key --pem <hex> raw private key (test nodes) node src/cli.js register --id <code> --address <0x..> node src/cli.js disburse --to <0x..> --amount <units> [--token <0x..>] [--memo text] node src/cli.js guardrails [--set maxPerTx=..,maxPerWindow=..,maxTxPerWindow=..,windowMs=..] node src/cli.js import --file <beneficiaries.csv> # id,address per row node src/cli.js batch --file <disbursements.csv> [--receipts out.csv] # id,amount[,memo] node src/cli.js balances node src/cli.js pending node src/cli.js report [--csv] node src/cli.js nonce --rpc <url> the one required net call node src/cli.js probe --rpc <url> node src/cli.js sync --rpc <url> broadcast pending in nonce order node src/cli.js demo
Flags: --data-dir <dir> (default: tmpdir/cashaid-data), --chain-id <id> (default 31337, the local hardhat node).
Architecture
| Piece | Tech | Role |
|---|---|---|
| Offline discovery & transport | Pear's ble-swarm | BLE radio, NoiseSecretStream connections; TCP fallback for dev/headless demos (same stream type, same protocol) |
| Local signed-tx log | Hypercore | Append-only, tamper-evident; replicates P2P between field-worker devices, preventing double-pay within the zone |
| Signing, nonce management, broadcast | WDK (@tetherto/wdk-wallet-evm) | Local offline signing; sendTransaction on sync |
| Chain choice | EVM (any account-nonce chain) | nonce-collision safety property |
| Packaging | Pear CLI | pear install pear://..., P2P OTA updates in the field |
Pear install
The desktop UI (see app/) is staged at version 4198:
pear://nkcy47zijnbrsi8oji7etuf6w14iiwgz89tsxca4b4rzyncp4fjy
To install (on any machine with Pear desktop):
pear install pear://nkcy47zijnbrsi8oji7etuf6w14iiwgz89tsxca4b4rzyncp4fjy
To restage after UI changes: from app/, run pear stage <link> . (a src -> ../src symlink plus the app's own node_modules make the bundle self-contained).
Roles
- Coordinator / field worker device — holds the funding wallet, runs the CLI, disburses to registered beneficiaries, advances the nonce sequence.
- Beneficiary device — WDK wallet, needs no signing and no native gas to receive (only to spend further; stretch).
- Merchant node (stretch) — accepts pending balances, settles once synced.
Spending guardrails
The WDK brief asks for spending guardrails — here they answer the third open question directly: how much can go wrong during the offline window? Before signing, every disbursement is checked against three caps (defaults in src/guardrails.js, adjustable via guardrails --set):
maxPerTx— ceiling on a single disbursement (default 5,000,000 base units)maxPerWindow— total signed within a rolling window (default 50,000,000)maxTxPerWindow— tx count within the window (default 20, window 24h)
A refusal happens before the nonce reservation, so it consumes no nonce and appends nothing to the ledger — double-spend exposure during the connectivity gap is bounded by the window caps, and a guardrail breach can never wedge the nonce sequence. Batch disbursement applies the caps per row: refused rows land in the receipt log with the reason, and the rest of the batch still signs.
Stretch goals delivered
- CSV import of beneficiary list (
import --file) + batch disbursement (batch --file) with a per-row receipt log — the scripted/batch flow the WDK brief asks for - Receipt/reconciliation report (
report --csv) - Spending guardrails (above)
- Local chained spending at a merchant: skipped per spec (chains on unconfirmed txs)
Out of scope for the hackathon: real biometric/ID verification, satellite uplink, production key custody for beneficiary wallets.
Open questions for judges / next steps
- (Done) Chain choice: EVM (any account-nonce chain would work)
- (Done) Spending cap per offline window: implemented as configurable guardrails
- How to run the demo script convincingly at the venue: local hardhat node = settlement endpoint; BLE radios only on venue hardware; TCP fallback documented for laptops
Hacki