Alpha · Experimental · Incomplete. Not for use on mainnet or with real funds. Testnet validation only — see
SPEC.md§11.1 for the implementation status of each promised feature.
Inspired by the words "Feed my sheep," Pretiola Pasture is a micro-donation platform that enables charitable ministries to receive EURC donations from arbitrary chains and settle them directly into EUR bank accounts via the Keeta Network.
apps/donor-web: Next.js donor interface for making contributions.services/orchestrator: Backend logic for ministry onboarding and ledger interaction.SPEC.md: Technical specification and architectural design (v0.3).registry.json: Registry of onboarded ministries.
Pretiola Pasture operates on a "Passive Ledger, Active Engine" model:
- The Secure Vault (Keeta Ledger): Acts as the passive source of truth. It holds the funds in Storage Accounts and enforces cryptographic rules about who can move them.
- The Active Engine (Pretiola Crank): Since the ledger has no internal clock, the Crank actively polls for inbound donations and "pushes" them to their final destination.
- The Administrative "Summoner": Pretiola uses a central administrative account to "summon" ministry accounts onto the ledger. It pays the required KTA gas fees so the ministry never has to touch a crypto wallet or manage native tokens.
- The Security Guard (HSM): The administrative account's signing power is restricted by a hardware-level policy (HSM), ensuring it can only move funds to a ministry's pre-verified bank account.
Before running the services, create a .env file in the root directory with the following variables:
ORCHESTRATOR_SEED=your_funded_seed
TESTNET_NETWORK=test
STORAGE_ACCOUNT_ADDRESS=keeta_ar4gm...
USDC_TOKEN_ADDRESS=keeta_apna75...
EURC_TOKEN_ADDRESS=keeta_api6tk...
IBAN_RECIPIENT_ADDRESS=keeta_aabh...USDC_TOKEN_ADDRESS is what the onboarding script uses today to scope SEND_ON_BEHALF, and it's what the crank sweeps. EURC_TOKEN_ADDRESS is kept for the mainnet path; see the Testnet Note below.
-
STORAGE_ACCOUNT_ADDRESS: This is generated automatically when you onboard a new ministry.- Run
cd services/orchestrator && node src/onboard-ministry.js. - Copy the
storageAccountAddressfrom the output into your.env.
- Run
-
IBAN_RECIPIENT_ADDRESS: This is the Keeta address for the IBAN Anchor (e.g., DEV2). In "True Settlement" mode, the Crank negotiates a session with this Anchor using real-world bank details (Name, BIC, IBAN) to generate a cryptographically bound settlement.- For testing, you can use the default Anchor address found in the
registry.json. - Copy the
boundIbanRecipientAddressfrom the registry into your.env.
- For testing, you can use the default Anchor address found in the
-
registry.json: This file stores the configuration and bank details for all onboarded ministries.- Copy the sample file to get started:
cp registry.json.sample registry.json
- Populate it with the real Keeta addresses and structured bank details (BIC, IBAN, etc.) for your target ministry.
- Copy the sample file to get started:
The front end is built with Next.js and provides the /give/[slug] routes for donors.
To start the development server:
cd apps/donor-web
npm run devThe application will be available at http://localhost:3000.
- Visit http://localhost:3000/give/sr-dolores to see the pilot foundation's page.
The orchestrator handles ministry onboarding and ledger verification. The crank handles the final settlement to the IBAN.
To run the onboarding script (for a new ministry):
cd services/orchestrator
node src/onboard-ministry.jsTo verify a donation has landed on the Keeta ledger:
node src/verify-donation.jsTo settle (sweep) funds from Storage to the bound IBAN (The Crank):
node src/reconciliation-crank.jsNote: This script now performs a True Settlement by negotiating with the IBAN Anchor using structured bank details (BIC, IBAN, Physical Address) and producing a block with a bound external session ID.
- Keeta SDK Validation (Spikes 1 & 2)
- Ministry Onboarding script (Sr. Dolores Foundation pilot) — seed generation still server-side; SPEC §4.1.3 requires browser-side, tracked as a Phase 2.1 hardening issue
- Rebranded Donor-Web with High-Contrast Aesthetic
- Automated Frontend Confirmation (Success Screen)
- Reconciliation Crank (Storage -> IBAN Sweep) — single ministry, balance snapshot only; no idempotency table, no chain-tail
- True Settlement structural plumbing (Structured bank details + Anchor
initiateTransfernegotiation + boundexternalID on the SEND block) - Software destination-binding check in the crank (Phase 2.1 stub for the future HSM policy)
- Holding sub-accounts + crank release / reverse-bridge (SPEC §5.3)
- Receiver ACL with tier predicates at the ledger (SPEC §5.2)
- Browser-side ministry key generation + escrow (SPEC §4.1.3)
- HSM / signing-service architecture (SPEC §7
infrastructure/kms/) - Webhook Hint Receiver (Architecture defined, using polling for MVP)
Testnet Note: Phase 2.1 settles in USDC, not EURC. The Keeta testnet IBAN Anchor (DEV2) does not currently expose an EURC route. The settlement asset is read from
USDC_TOKEN_ADDRESSin.envand parameterizes theSEND_ON_BEHALFgrant at onboarding; mainnet cutover swaps the token id and re-onboards each ministry with a new EURC-scoped grant. The donor UI labels contributions as "USDC" in Phase 2.1 to keep donor expectations honest.
Important for the Pilot:
In Phase 2.1, the SEND_ON_BEHALF permission is token-scoped at the ledger level. This means the Keeta protocol itself does not prevent the operator key from sending funds to a different address. There are two layers of mitigation today and a third on the roadmap:
- Software destination-binding check (current): Before publishing the SEND block, the reconciliation crank verifies that the
sendToAddressreturned by the IBAN Anchor matchesministry.boundIbanRecipientAddressfrom the registry. A misbehaving or misconfigured anchor cannot trick the crank into signing a sweep to an unbound address. This is the SPEC's "policy" rule restated in JavaScript — defense-in-depth, not a cryptographic guarantee. .env-loaded operator key (current, the gap): TheORCHESTRATOR_SEEDis loaded into the orchestrator process from.env. An attacker who roots the orchestrator host or reads the file gets full sweep authority within the bounds of the destination check — i.e., they can replay/retry sweeps but cannot redirect funds. They cannot reach the OWNER role (key not held by Pretiola).- HSM / signing-service (Production roadmap): The operator key moves into a Cloud KMS, CloudHSM, or attested enclave (Nitro Enclave / Confidential VM). The destination-binding check moves into the same trust domain as the key. At that point, "refuse to sign unauthorized destinations" becomes a hardware/cloud-enforced rule, and a compromised orchestrator host can no longer produce signatures.
The architectural choice between KMS-managed key + sidecar policy vs. in-enclave key with attested boot image is open and tracked as a design issue.