Skip to content

Hera-Commons/hera

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hera — Medical Dataset Exchange on XRPL

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.


The Problem

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.

The Solution

Hera introduces a sealed computation model:

  1. The provider seals their dataset locally (hash + secret seed). Raw data never leaves their machine.
  2. Metadata (description, available query types, price) is published on-chain.
  3. 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.
  4. 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.
  5. 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 Compliance Argument

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

Production Architecture (v2 Vision)

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.

Layer 1 — Identity

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."

Layer 2 — Dataset Registry

Each dataset is represented by an NFToken minted on the provider's XRPL account:

  • The NFT URI field 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.

Layer 3 — Access Control

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.

Layer 4 — Verifiable Compute

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.

Layer 5 — Trustless Settlement

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.

Architecture diagram (v2)

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
Loading

Hackathon Implementation (v1)

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.

v1 vs v2 — Simplification Map

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

What the signature proof actually guarantees (v1)

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.

What the time-based escrow actually guarantees (v1)

Without a Condition field, the escrow is time-gated only:

  • Provider can call EscrowFinish once FinishAfter passes (30 seconds after creation), with no proof of delivery required.
  • Consumer can call EscrowCancel after CancelAfter (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.

v1 Architecture diagram

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
Loading

Proof Mechanism (v1)

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:

  1. Recompute the SHA-256 from the on-chain fields
  2. Call ripple-keypairs.verify(payload, signature, providerPublicKey) — returns true or false

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.


Escrow Mechanism (v1)

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: EscrowCancel only succeeds after CancelAfter has 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 vs v2 Escrow Flow Comparison

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.
Loading

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.
Loading

On-Chain Data Structures

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.

hera/dataset — provider publishes to registry

{
  "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"
}

hera/request — consumer posts to provider

{
  "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"
}

hera/response — provider posts to consumer (happy path)

{
  "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"
}

hera/rejection — provider posts to consumer (reject path)

{
  "requestId": "b7c2d3e4f5a6b7c8",
  "reason": "Dataset temporarily unavailable",
  "escrowCancelAfter": 1234654290,
  "rejectedAt": "2026-04-11T09:06:00.000Z"
}

Local Data Structures

Sealed dataset record (datasets/<id>.json) — provider only

{
  "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.

Local request record (datasets/request-<id>.json) — consumer only

{
  "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.


Installation

git clone <repo>
cd hera
npm install
chmod +x src/cli.js

CLI Reference

Identity management

# Create a new identity (wallet keypair)
hera identity create <name>

# List all local identities
hera identity list

After creating an identity, fund the address using the testnet faucet:

https://test.bithomp.com/en/faucet

Dataset management (provider)

# 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...

Compute requests — consumer side

# 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>

Compute requests — provider side

# 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>]

Result verification

# 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...

End-to-End Demo Flow

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>

Reject flow

# 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>

REST API Reference

Start the server:

npm run server
# or: node src/server.js
# Default port: 3000. Override with PORT env var.

Calling endpoints from the repo (.http files)

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

  1. Start the API (npm run server) so it listens on http://localhost:3000 (or align the @baseUrl variable at the top of each file with your PORT).
  2. Install a REST Client extension that understands the .http format. 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.
  3. Open a .http file 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" }

Identity

Method Path Body Description
POST /identities { name } Create identity
GET /identities List all identities

Dataset

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

Requests

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

GET /requests?provider-identity=<name> response

{
  "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"
    }
  ]
}

GET /requests/:requestId?provider=<address> response

{ "ok": true, "requestId": "...", "status": "pending" }
{ "ok": true, "requestId": "...", "status": "responded", "response": { "result": "...", "proof": "..." } }
{ "ok": true, "requestId": "...", "status": "rejected",  "rejection": { "reason": "...", "escrowCancelAfter": 12345 } }

Results

Method Path Query Description
GET /results/:requestId/verify ?provider=<address> Verify result signature on-chain

Available Mock Queries

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

XRPL Primitives Used

v1 (Hackathon)

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

v2 (Production additions)

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)


File Structure

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

Pitch Points for Judges

Core value proposition

  • 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.

Why XRPL

  • EscrowCreate / EscrowFinish is 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.
  • CancelAfter enforces 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.

Compliance narrative

"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."

Gap → opportunity

  • 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.

v1 → v2 upgrade path is additive

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.


Explorer Links

  • Testnet explorer: https://test.bithomp.com/en/<address or tx hash>
  • Testnet faucet: https://test.bithomp.com/en/faucet

About

Medical Dataset Exchange on XRPL

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors