Paris Blockchain Week Hackathon 2026 — "Make Waves / Impact Finance" track
A protocol for compliant exchange of medical datasets between clinics and research institutions. Providers keep their raw data; consumers get cryptographically-verifiable query results. Payment is trustless via XRPL escrow.
Healthcare institutions sit on enormous datasets that could drive life-saving research — but GDPR, HIPAA, and institutional data-sharing agreements make direct exchange legally and technically intractable. Data brokerage through third parties creates new privacy surfaces. The result: petabytes of idle, siloed medical data.
Hera introduces a sealed computation model:
- The provider seals their dataset locally (hash + secret seed). Raw data never leaves their machine.
- Metadata (description, available query types, price) is published on-chain.
- A consumer discovers the dataset on-chain, opens an escrow, and sends a named compute request — a query from a pre-approved list with specific parameters.
- The provider runs the query locally and returns only the aggregated result (e.g.
COUNT: 142), along with a cryptographic proof that the result corresponds to the sealed dataset. - The escrow resolves and payment is released atomically.
No raw data is transmitted. No new privacy surface is introduced beyond what the provider already manages internally. Every request and response is permanently anchored on the XRPL ledger.
| GDPR Principle | How Hera addresses it |
|---|---|
| Data minimisation | Only aggregate query results are shared, never individual records |
| Purpose limitation | Only named, pre-published queries can be executed — arbitrary access is structurally impossible |
| Accountability | Every request and response is timestamped and anchored on an immutable ledger |
| No new data controller | Provider remains the sole data controller; Hera is a protocol, not a processor |
| Revocability | Provider can reject any request on-chain; unanswered escrows auto-cancel after 24h |
This section describes the target architecture for a production release. None of it is blocked by missing technology — every component referenced exists today. The hackathon implements a simplified subset, described in the next section.
Each participant (provider and consumer) is identified by a W3C DID anchored on XRPL:
did:xrpl:1:<address>
The provider's DID Document (stored via a DIDSet transaction) contains their public key, service endpoint, and credential status. Identity is portable and key-rotation is possible without changing the address.
Verifiable Credentials (W3C VC Data Model) issued by trusted authorities (hospital accreditation bodies, GDPR compliance auditors) are attached to the DID. A credential might assert: "this institution has signed a Data Processing Agreement valid through 2027" or "this provider's dataset has been certified by EU health authority X."
Each dataset is represented by an NFToken minted on the provider's XRPL account:
- The NFT
URIfield points to an IPFS document containing: schema, available queries, pricing, and the dataset's Merkle root commitment. - The raw dataset and its secret seed stay on the provider's hardware — they never appear anywhere on-chain or in the IPFS document.
- Consumer discovery uses
account_nfts(an indexed XRPL RPC call, O(dataset count)) rather than a full transaction history scan. - Burning the NFT constitutes dataset withdrawal; minting a replacement with a new URI is a dataset update.
Before any compute request is accepted, the consumer must hold a valid XRPL Credential (XLS-70 amendment, live on testnet):
CredentialCreate (issued by Hera Registry account)
Subject: consumer_address
CredentialType: "hera-consumer-v1"
Expiration: 1 year
CredentialAccept (signed by consumer)
In production, the Registry issues credentials only to institutions that have completed off-chain onboarding: identity verification, DPA signing, and jurisdiction compliance check. The credential lives in XRPL ledger state. The provider checks account_credentials at request time — no API call to the Registry is needed. An uncredentialed request is rejected before any computation runs.
This turns GDPR access control from an application-level convention into a protocol-level enforcement.
The provider runs the query inside a RISC Zero zkVM program:
inputs: dataset rows + Merkle inclusion proofs + query params
outputs: result + ZK proof of correct computation
The proof attests, without revealing any individual record: "this aggregate result is the correct output of this exact query on the dataset committed to by this Merkle root."
Boundless (RISC Zero's decentralized prover marketplace) can serve as the proving layer — the provider submits a proof request, competitive provers generate the proof, and the provider pays in the proof market's token. This decouples computation from proving and enables trustless outsourcing of the ZK work.
Note: Merkle inclusion proofs alone are insufficient for privacy — revealing a row to prove it's in the set violates GDPR. The zkVM consumes the rows internally as private inputs; only the proof is published.
The escrow uses a full ASN.1-encoded SHA-256 crypto-condition (Condition field on EscrowCreate). The flow is two-phase:
1. Consumer posts request (no escrow yet)
2. Provider runs query, encrypts result with key K, posts commitment H = SHA256(K)
3. Consumer creates EscrowCreate with Condition = H
4. Provider submits EscrowFinish with Fulfillment = K
→ K appears on-chain; consumer decrypts result; provider gets paid — atomically
In the ZKP variant, K is the proof itself (or a hash of it). With XRPL Hooks, a Hook attached to the escrow account can verify the ZK proof inline and auto-release the escrow without any trusted party.
flowchart TD
subgraph L1["① Identity"]
A["DIDSet tx · did:xrpl:1:[address]<br/>W3C Verifiable Credentials from accreditation bodies"]
end
subgraph L2["② Dataset Registry"]
B["NFTokenMint · URI → IPFS (schema, queries, Merkle root)<br/>account_nfts() — O(dataset count) discovery"]
end
subgraph L3["③ Access Control · XRPL Credentials (XLS-70)"]
C["Registry issues hera-consumer-v1<br/>Provider checks account_credentials at query time"]
end
subgraph L4["④ Verifiable Compute"]
D["RISC Zero zkVM (rows as private inputs) + Boundless prover market<br/>ZK proof: result = f(committed_dataset, query)"]
end
subgraph L5["⑤ Trustless Settlement"]
E["EscrowCreate(Condition = ZK commitment) → EscrowFinish(Fulfillment = proof)<br/>XRPL Hook verifies proof inline, auto-releases escrow"]
end
A --> B --> C --> D --> E
The hackathon implements the full protocol flow end-to-end, with deliberate simplifications in each layer. Every shortcut is described below alongside the production replacement. The core mechanic — seal, publish, request, compute, verify, pay — is complete and running on XRPL testnet.
| Concern | v1 (Hackathon) | v2 (Production) | Why simplified |
|---|---|---|---|
| Identity | XRPL keypair address only | did:xrpl:1:<addr> + DID document + W3C VCs |
DID tooling setup is non-trivial; core mechanic doesn't depend on it |
| Dataset registry | hera/dataset Memo on Payment tx |
NFToken with URI → IPFS metadata | Memos are write-once events with no native index; NFTs have account_nfts |
| Dataset discovery | Scan account_tx history, filter client-side |
account_nfts RPC (indexed, O(dataset count)) |
account_tx scan is O(all transactions); fine for demo scale |
| Access control | None — any funded address can request | XRPL Credentials (XLS-70): DPA-gated credential per consumer | Credential issuance requires registry account + off-chain onboarding flow |
| Query engine | Deterministic mocks — CSV is not parsed | Real in-process query engine over sealed CSV | Demonstrates the protocol; real queries are engineering, not architecture |
| Compute proof | Provider signs SHA-256(dataset|query|params|result) | RISC Zero ZK proof that result = correct_query(committed_dataset) | ZK toolchain integration is multi-week work; signature proves accountability |
| Escrow condition | Time-based only: FinishAfter + CancelAfter |
Full ASN.1 crypto-condition: provider must commit to result to claim payment | ASN.1 encoding via five-bells-condition omitted for demo simplicity |
| Deployment | Both parties on same machine, shared filesystem | Independent machines; coordination only through on-chain data | Removes the operational complexity of two live funded testnet accounts |
The v1 proof is accountability, not cryptographic correctness. The provider signs:
SHA-256( dataset-id | query-name | params-json | result )
This proves the result was produced by the entity controlling the provider keypair and has not been tampered with after signing. It does not prove the result was correctly computed on the committed dataset — a dishonest provider could sign a fabricated number. The trust model for v1 is: providers are identified institutions with reputational and legal skin in the game. The ZKP in v2 removes this trust assumption entirely.
Without a Condition field, the escrow is time-gated only:
- Provider can call
EscrowFinishonceFinishAfterpasses (30 seconds after creation), with no proof of delivery required. - Consumer can call
EscrowCancelafterCancelAfter(24 hours) if the provider is silent. - A dishonest consumer who receives a result can wait 24 hours and reclaim funds via
EscrowCancel.
The escrow guarantees non-delivery protection (provider cannot get paid for doing nothing indefinitely) but not delivery atomicity (the exchange of result for payment is not simultaneous). V2's crypto-condition makes it atomic: the provider's EscrowFinish fulfillment IS the result commitment, so payment and result delivery happen in the same ledger close.
sequenceDiagram
autonumber
participant P as Provider
participant X as XRPL Testnet
participant C as Consumer
participant V as Verifier (anyone)
rect rgb(230,243,255)
Note over P,X: Setup — seal & publish
P->>P: dataset seal — SHA-256(csv + seed), stored locally only
P->>X: Payment (1 drop) + hera/dataset memo → REGISTRY_ADDRESS
end
rect rgb(230,255,230)
Note over C,X: Discovery
C->>X: account_tx(providerAddress)
X-->>C: hera/dataset memos — schema, queries, price
end
rect rgb(255,250,220)
Note over C,X: Request
C->>X: EscrowCreate — 2 XRP, FinishAfter +30s, CancelAfter +24h
C->>X: Payment (1 drop) + hera/request memo → providerAddress
C->>C: save request state locally (preimage, escrowSequence)
end
rect rgb(255,230,230)
Note over P,X: Respond
P->>X: account_tx(self) — scan for hera/request memos
X-->>P: request (requestId, queryName, params, escrowSequence)
P->>P: run mock query, sign SHA-256(dataset|query|params|result)
P->>X: Payment (1 drop) + hera/response memo → consumerAddress
P->>X: EscrowFinish(offerSequence)
X-->>P: 2 XRP released ✓
end
rect rgb(240,230,255)
Note over V,X: Verify (any party)
V->>X: account_tx(providerAddress) — find hera/response
X-->>V: result + proof + providerPublicKey
V->>V: ripple-keypairs.verify(payload, proof, pubKey) → true ✓
end
The provider signs:
SHA-256( dataset-id | query-name | params-json | result )
with their XRPL private key via ripple-keypairs.sign(). The signature and payload are both posted on-chain in the hera/response memo. Any party can:
- Recompute the SHA-256 from the on-chain fields
- Call
ripple-keypairs.verify(payload, signature, providerPublicKey)— returnstrueorfalse
This proves the result was produced by the entity controlling the provider keypair and has not been tampered with after signing.
v2 upgrade path: Replace the signature with a RISC Zero ZK proof. The zkVM program takes (dataset_rows, merkle_proofs, query_params) as private inputs and outputs (result, proof). The proof is constant-size (~200 bytes for Groth16) and verifiable without any knowledge of the underlying data.
Consumer:
EscrowCreate:
Amount = dataset.priceXrp
Destination = providerAddress
FinishAfter = now + 30s ← required field; covers ledger close delay
CancelAfter = now + 24h ← consumer safety window
Happy path — provider responds:
Posts hera/response memo with result + proof
Submits EscrowFinish → payment released
Reject path — provider rejects:
Posts hera/rejection memo to consumer
Attempts EscrowCancel immediately (succeeds only if CancelAfter has passed)
If still within 24h window: consumer runs `request cancel` after CancelAfter
Consumer safety:
EscrowCancel can be submitted by anyone after CancelAfter — no trust in provider required
XRPL constraint:
EscrowCancelonly succeeds afterCancelAfterhas passed. This is unconditional — not even the escrow owner can cancel early. The provider cannot yank funds mid-computation, and the consumer cannot cancel while the provider is still running the query.
v2 upgrade path: Two-phase flow with full ASN.1 crypto-condition. Provider runs query, encrypts result with key K, publishes H = SHA256(K) on-chain. Consumer creates EscrowCreate(Condition=H). Provider calls EscrowFinish(Fulfillment=K) — K appears on-chain atomically with payment, consumer decrypts result. Neither party can cheat: provider cannot claim payment without revealing K; consumer receives K exactly when provider is paid. With XRPL Hooks, the escrow account can verify the ZK proof inline and self-release.
v1 — Time-based only (no atomicity guarantee)
sequenceDiagram
participant C as Consumer
participant X as XRPL
participant P as Provider
C->>X: EscrowCreate (FinishAfter +30s, CancelAfter +24h)
C->>X: hera/request memo → provider
P->>X: hera/response memo → consumer (result + signature)
P->>X: EscrowFinish(offerSequence)
X-->>P: payment released
Note over C,P: ⚠️ Consumer received result before EscrowFinish confirmed.<br/>A dishonest consumer could wait 24 h and reclaim via EscrowCancel.
v2 — Crypto-condition two-phase (atomic delivery)
sequenceDiagram
participant C as Consumer
participant X as XRPL
participant P as Provider
P->>P: run query, encrypt result with key K
P->>X: hera/response memo → consumer (encrypted result + commitment H = SHA-256(K))
C->>X: EscrowCreate (Condition = H, CancelAfter +24h)
P->>X: EscrowFinish (Fulfillment = K)
Note over X: validates SHA-256(K) == H ✓
X-->>P: payment released
Note over C,X: K appears on-chain atomically with payment.<br/>Consumer decrypts result in the same ledger close. Neither party can cheat.
All on-chain data is carried in Memo fields of ordinary XRP payment transactions (1 drop each). No custom ledger objects are required.
Dataset publish memos are sent to a shared registry address (rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh) rather than to self — XRPL rejects self-payments as temREDUNDANT. The memo still appears in the provider's account_tx history for consumer discovery.
v2 note: In production, the hera/dataset memo is replaced by an NFToken. The on-chain data structures for requests and responses (memos on Payment transactions) remain the same in v2.
{
"id": "a3f8c1d2e4b5f6a7",
"name": "EU ICU Admissions Q1 2024",
"description": "ICU admission records from three EU hospitals, anonymised, Q1 2024",
"provider": "rProviderAddress...",
"priceXrp": "2",
"contentHash": "sha256hex...",
"queries": [
{
"name": "count_by_diagnosis",
"description": "Count records matching a diagnosis code (ICD-10)",
"params": ["diagnosis_code"],
"example": { "diagnosis_code": "J18.9" }
},
{
"name": "age_distribution",
"description": "Histogram of patient ages grouped into 10-year buckets",
"params": [],
"example": {}
},
{
"name": "gender_split",
"description": "Count of records split by gender (M/F)",
"params": [],
"example": {}
},
{
"name": "avg_stay_by_diagnosis",
"description": "Average hospital stay duration (days) for a diagnosis code",
"params": ["diagnosis_code"],
"example": { "diagnosis_code": "I21.0" }
}
],
"publishedAt": "2026-04-11T09:00:00.000Z"
}{
"requestId": "b7c2d3e4f5a6b7c8",
"datasetId": "a3f8c1d2e4b5f6a7",
"providerAddress": "rProviderAddress...",
"consumerAddress": "rConsumerAddress...",
"queryName": "avg_stay_by_diagnosis",
"params": { "diagnosis_code": "I21.0" },
"condition": "SHA256HEXOFPREIMAGE...",
"escrowSequence": 42,
"escrowTxHash": "TXHEX...",
"escrowFinishAfter": 1234567890,
"escrowCancelAfter": 1234654290,
"createdAt": "2026-04-11T09:05:00.000Z"
}{
"requestId": "b7c2d3e4f5a6b7c8",
"datasetId": "a3f8c1d2e4b5f6a7",
"queryName": "avg_stay_by_diagnosis",
"params": { "diagnosis_code": "I21.0" },
"result": "AVG_STAY(diagnosis_code='I21.0'): 9.8 days",
"proof": "SIGNATUREHEX...",
"proofPayload": "SHA256HEX...",
"providerPublicKey": "ED...",
"preimage": "PREIMAGE32BYTESHEX...",
"respondedAt": "2026-04-11T09:07:00.000Z"
}{
"requestId": "b7c2d3e4f5a6b7c8",
"reason": "Dataset temporarily unavailable",
"escrowCancelAfter": 1234654290,
"rejectedAt": "2026-04-11T09:06:00.000Z"
}{
"id": "a3f8c1d2e4b5f6a7",
"name": "EU ICU Admissions Q1 2024",
"description": "ICU admission records, EU hospitals, Q1 2024",
"priceXrp": "2",
"csvPath": "./mock-data/eu_icu_admissions_synthetic_2024.csv",
"seed": "RANDOMHEX...",
"contentHash": "SHA256HEX...",
"sealedAt": "2026-04-11T08:55:00.000Z",
"published": true,
"publishTxHash": "TXHEX...",
"queries": [ "..." ]
}The seed field is never published. It prevents pre-image attacks where an adversary brute-forces the dataset hash by trying known medical record combinations.
{
"requestId": "b7c2d3e4f5a6b7c8",
"preimage": "PREIMAGE32BYTESHEX...",
"condition": "SHA256HEXOFPREIMAGE...",
"escrowSequence": 42,
"escrowFinishAfter": 1234567890,
"escrowCancelAfter": 1234654290,
"providerAddress": "rProviderAddress...",
"consumerAddress": "rConsumerAddress...",
"queryName": "avg_stay_by_diagnosis",
"params": { "diagnosis_code": "I21.0" }
}The preimage is stored locally only. In the v1 single-machine simulation it is read by the provider from the shared filesystem — a known shortcut. In v2, the two-phase escrow flow replaces this entirely: the provider generates their own commitment and the preimage concept is no longer needed on the consumer side.
git clone <repo>
cd hera
npm install
chmod +x src/cli.js# Create a new identity (wallet keypair)
hera identity create <name>
# List all local identities
hera identity listAfter creating an identity, fund the address using the testnet faucet:
https://test.bithomp.com/en/faucet
# Seal a CSV dataset locally — hashes with a secret seed. No network, nothing on-chain.
# <csv-file> can be an absolute path, a relative path, or a bare filename
# resolved automatically from mock-data/ (e.g. just "eu_icu_admissions_synthetic_2024.csv")
hera dataset seal <name> <csv-file> [--price <xrp>] [--description <text>]
# Publish dataset metadata on-chain.
# --price and --description fall back to values set at seal time if omitted.
hera dataset publish <identity> <dataset-name> [--price <xrp>] [--description <text>]
# List all datasets published by a provider address
hera dataset list <provider-address>
# Show full metadata + available queries for a specific dataset
hera dataset show <provider-address> <dataset-id>Example:
hera dataset seal "EU ICU Q1 2024" eu_icu_admissions_synthetic_2024.csv \
--price 2 --description "ICU admissions, EU hospitals, synthetic, 2024"
hera dataset publish clinic1 "EU ICU Q1 2024"
hera dataset list rClinic1Address...# Open an escrow and post a compute request on-chain.
# Returns a requestId to track the response.
hera request create <consumer-identity> <provider-address> <dataset-id> <query-name> \
[--param key=value ...]
# Check whether the provider has responded or rejected
hera request status <provider-address> <request-id>
# Cancel an escrow and reclaim funds.
# Only succeeds after the 24h CancelAfter window — XRPL enforces this unconditionally.
hera request cancel <consumer-identity> <request-id># List all incoming requests with their current status (pending / responded / rejected)
hera request list <provider-identity>
# Run the query, sign the result, post response on-chain, finish the escrow
hera request respond <provider-identity> <request-id>
# Reject a request — posts a hera/rejection memo to the consumer and attempts EscrowCancel.
# If still within the 24h window, the memo is posted but the escrow stays open.
# The consumer can then run `request cancel` once CancelAfter passes.
hera request reject <provider-identity> <request-id> [--reason <text>]# Fetch the response from on-chain and verify the provider's signature.
# Can be run by the consumer, a regulator, or any third party.
hera result verify <provider-address> <request-id>Output:
VERIFIED
requestId: b7c2d3e4
dataset: a3f8c1d2e4b5f6a7
query: avg_stay_by_diagnosis
params: {"diagnosis_code":"I21.0"}
result: AVG_STAY(diagnosis_code='I21.0'): 9.8 days
signature: valid
tx: ABCD1234...
Both provider and consumer run from the same machine.
# 1. Create identities
hera identity create clinic1
hera identity create clinic2
# 2. Fund both addresses using testnet faucet (manual)
# https://test.bithomp.com/en/faucet
# 3. Provider: seal the sample dataset
hera dataset seal "EU ICU Q1 2024" eu_icu_admissions_synthetic_2024.csv \
--price 2 --description "ICU admissions, EU hospitals, synthetic, 2024"
# 4. Provider: publish metadata on-chain
hera dataset publish clinic1 "EU ICU Q1 2024"
# → note the dataset ID printed
# 5. Consumer: browse available datasets
hera dataset list <clinic1-address>
# 6. Consumer: inspect a dataset's available queries
hera dataset show <clinic1-address> <dataset-id>
# 7. Consumer: submit a compute request
hera request create clinic2 <clinic1-address> <dataset-id> avg_stay_by_diagnosis \
--param diagnosis_code=I21.0
# → note the request ID printed
# 8. Provider: see all pending requests
hera request list clinic1
# 9. Provider: respond (run query, sign, post response, finish escrow)
hera request respond clinic1 <request-id>
# 10. Consumer: check status
hera request status <clinic1-address> <request-id>
# 11. Anyone: verify the result independently
hera result verify <clinic1-address> <request-id># Provider rejects instead of responding
hera request reject clinic1 <request-id> --reason "Dataset under maintenance"
# → posts hera/rejection memo
# → attempts EscrowCancel (will note if still within 24h window)
# Consumer checks status — sees rejection
hera request status <clinic1-address> <request-id>
# Consumer reclaims funds after CancelAfter passes (24h from escrow creation)
hera request cancel clinic2 <request-id>Start the server:
npm run server
# or: node src/server.js
# Default port: 3000. Override with PORT env var.The http/ folder contains ready-made requests you can run from the editor without curl:
| File | Purpose |
|---|---|
http/phase0-identities.http |
Create demo identities (POST /identities, health check). |
http/phase1-datasets.http |
Seal and publish datasets (/datasets/...). |
http/phase1-requests.http |
Consumer/provider request flow (/requests/...). |
How to use them
- Start the API (
npm run server) so it listens onhttp://localhost:3000(or align the@baseUrlvariable at the top of each file with yourPORT). - Install a REST Client extension that understands the
.httpformat. In VS Code / Cursor, the usual choice is REST Client (humao.rest-client). JetBrains IDEs ship with an HTTP Client that can run the same files. - Open a
.httpfile and use Send Request (or the gutter action) above each request block to execute it and see the response in the editor.
The files include short comments for ordering (e.g. fund addresses on Testnet after phase 0, then edit shared variables before phase 1).
All responses:
{ "ok": true, ... }
{ "ok": false, "error": "message" }| Method | Path | Body | Description |
|---|---|---|---|
POST |
/identities |
{ name } |
Create identity |
GET |
/identities |
— | List all identities |
| Method | Path | Body / Query | Description |
|---|---|---|---|
POST |
/datasets/seal |
{ name, csvPath, priceXrp?, description? } |
Seal CSV locally |
POST |
/datasets/publish |
{ identity, name, priceXrp?, description? } |
Publish metadata on-chain |
GET |
/datasets?provider=<address> |
— | List all datasets for a provider |
GET |
/datasets/:datasetId?provider=<address> |
— | Get full metadata + queries |
| Method | Path | Body / Query | Description |
|---|---|---|---|
POST |
/requests |
{ identity, providerAddress, datasetId, queryName, params? } |
Open escrow + post request |
GET |
/requests?provider-identity=<name> |
— | (Provider) List all incoming requests with status |
GET |
/requests/:requestId?provider=<address> |
— | (Consumer) Get status of a specific request |
POST |
/requests/:requestId/respond |
{ identity } |
(Provider) Run query, sign, finish escrow |
POST |
/requests/:requestId/reject |
{ identity, reason? } |
(Provider) Reject request, post memo, attempt cancel |
POST |
/requests/:requestId/cancel |
{ identity } |
(Consumer) Cancel escrow after CancelAfter |
{
"ok": true,
"requests": [
{
"requestId": "b7c2d3e4...",
"consumerAddress": "rConsumerAddress...",
"datasetId": "a3f8c1d2...",
"queryName": "avg_stay_by_diagnosis",
"params": { "diagnosis_code": "I21.0" },
"createdAt": "2026-04-11T09:05:00.000Z",
"status": "pending"
}
]
}{ "ok": true, "requestId": "...", "status": "pending" }
{ "ok": true, "requestId": "...", "status": "responded", "response": { "result": "...", "proof": "..." } }
{ "ok": true, "requestId": "...", "status": "rejected", "rejection": { "reason": "...", "escrowCancelAfter": 12345 } }| Method | Path | Query | Description |
|---|---|---|---|
GET |
/results/:requestId/verify |
?provider=<address> |
Verify result signature on-chain |
| Query name | Description | Params |
|---|---|---|
count_by_diagnosis |
Count records matching an ICD-10 code | diagnosis_code |
age_distribution |
Age histogram in 10-year buckets | (none) |
gender_split |
M/F record counts | (none) |
avg_stay_by_diagnosis |
Average hospital stay (days) for a diagnosis | diagnosis_code |
| Primitive | Purpose |
|---|---|
Payment with Memo |
Dataset publish, compute request, compute response, rejection |
EscrowCreate |
Consumer locks payment; FinishAfter + CancelAfter set the time bounds |
EscrowFinish |
Provider claims payment after delivering result |
EscrowCancel |
Anyone cancels the escrow after CancelAfter — returns funds to consumer |
account_tx RPC |
Scan account transaction history for memo-bearing transactions |
| Primitive | Purpose |
|---|---|
DIDSet |
Anchor provider/consumer DID document on-chain |
NFTokenMint / NFTokenBurn |
Dataset registration and withdrawal |
CredentialCreate / CredentialAccept |
Registry issues access credentials; consumers accept them |
account_nfts RPC |
Indexed dataset discovery — O(dataset count), not O(all transactions) |
account_credentials RPC |
Provider checks consumer authorization at request time |
EscrowCreate with Condition |
Full ASN.1 crypto-condition for atomic result-for-payment exchange |
| XRPL Hooks | Inline ZK proof verification; auto-release escrow on valid proof |
Network: wss://s.altnet.rippletest.net:51233 (XRPL testnet)
hera/
├── CLAUDE.md
├── README.md
├── .gitignore ← keystore/, datasets/, node_modules/, IDE/OS artifacts
├── package.json (xrpl ^4.6.0, commander ^12, express ^4)
├── src/
│ ├── cli.js ← CLI entry point (commander), all commands
│ ├── server.js ← Express REST API, mirrors CLI 1:1
│ ├── identity.js ← Keypair generation, keystore management
│ ├── dataset.js ← Seal (with path resolution), publish, list, show
│ ├── request.js ← Create, list, status, reject, cancel
│ ├── respond.js ← Mock query engine, ripple-keypairs proof, EscrowFinish
│ ├── verify.js ← On-chain response fetch + ripple-keypairs verification
│ ├── chain.js ← Memo encode/decode, postMemo(), scanMemos(), MEMO_TYPES
│ └── xrpl-client.js ← withClient(), submitAndWait() with tec* guard
├── keystore/ ← Keypair files (gitignored)
├── datasets/ ← Sealed dataset records + request state (gitignored)
├── http/
│ ├── phase0-identities.http ← REST Client: identities + health
│ ├── phase1-datasets.http ← seal / publish / list / show
│ └── phase1-requests.http ← create / list / status / respond / reject
├── tools/
│ └── generate-dataset.js ← Regenerates mock-data CSV (node tools/generate-dataset.js)
└── mock-data/
└── eu_icu_admissions_synthetic_2024.csv ← 100 synthetic patient records, 42 columns
- Zero new privacy surface. The provider already holds the data. Hera does not route, copy, or expose any raw records. The only thing that moves is an aggregated count or average — the same number a hospital would publish in a press release.
- Trustless payment. XRPL escrow ensures the provider is paid if and only if they deliver a result. No intermediary, no invoice, no 30-day payment terms.
- Permanent audit trail. Every request, response, and rejection is cryptographically anchored on the XRPL ledger with a timestamp. Regulators can audit the entire exchange history without any cooperation from the parties.
- Provider control. Providers can reject requests on-chain, with the rejection memo serving as a permanent record of the decision.
EscrowCreate/EscrowFinishis a first-class ledger primitive — no smart contract deployment, no gas estimation, no Solidity bugs.- Memo fields provide cheap, permanent on-chain anchoring for structured metadata without custom ledger objects.
- 3–5 second finality means the demo round trip is fast enough to show live on stage.
CancelAfterenforces the consumer's 24h refund window at the protocol level — not in application code.- XRPL Credentials (live on testnet) turn GDPR access control into a protocol-level enforcement, not an application convention.
- XRPL Hooks (Xahau) enable inline ZK proof verification, making escrow release fully autonomous.
"We don't ask hospitals to trust each other. We don't ask them to trust us. We ask them to trust SHA-256 and secp256k1 — the same primitives that secure the global financial system."
- Current state: data sharing agreements take 6–18 months of legal negotiation per bilateral pair.
- Hera reduces this to: deploy an identity, seal your dataset, set a price. The legal surface is minimal because no raw data changes hands.
Every v1 component has a direct v2 replacement with the same interfaces:
- Memo → NFToken (same dataset ID, same query definitions, better discoverability)
- Signature → ZK proof (same result format, stronger guarantee)
- Time escrow → Crypto-condition escrow (same EscrowCreate/Finish flow, atomic delivery)
- No access control → XRPL Credentials (new pre-request check, no changes to request/response flow)
The protocol design is stable. The hackathon submission demonstrates the complete flow; production hardens each layer independently.
- Testnet explorer:
https://test.bithomp.com/en/<address or tx hash> - Testnet faucet:
https://test.bithomp.com/en/faucet