CRSP · Normative Routing Contract (NRC) · Ethereum Operations

Deploy NRCRegistry.sol, Tokenize Entitlements, Anchor AER On-Chain

This page is an operational blueprint for running CRSP’s NRC on an Ethereum network: deploy the NRCRegistry, optionally issue Entitlement Tokens, and anchor the AER loop (Ask → Evidence → Entitle → Release) as tamper-evident on-chain events — without putting PHI/PII or model content on-chain.

On-chain: hashes + role attestations Off-chain: evidence bundles + PHI/PII Controller: Safe multisig Recommended: L2 (Base/OP/Arbitrum)

Operating contract surface

Contract

NRCRegistry.sol

Optional

EntitlementToken.sol

Events

NRCEvent(instanceKey, eventType, eventHash, payloadHash, prevHash, newState)

Anchor rule

Never store evidence content on-chain. Store hashes + pointers.
Regulated-domain default: On-chain logs are for integrity, not content. All prompts, outputs, PHI/PII, and evidence documents remain in governed storage.

1) Architecture snapshot

CRSP runs the normative engine off-chain (policy, evidence, accountability). Ethereum records durable commitments (hash receipts and role attestations).

Components

  • Evidence Vault (Unity Catalog / encrypted object store): evidence bundles, audit notes, model traces (no on-chain)
  • Anchor Service (internal): canonicalize → hash → submit tx via controller
  • Controller (Safe multisig): enforces role-based approvals for ENTITLE/RELEASE
  • NRCRegistry (Ethereum): stores template/instance hashes + emits event receipts
  • Entitlement Token (optional): ERC-1155 / ERC-721 non-transferable “receipt token”

Event integrity rule

EventHash = keccak256(canonical(NRC_Event_Header_JSON))
Canonicalization must be stable (sorted keys, normalized timestamps, fixed field names).

2) Deploy NRCRegistry.sol (Foundry)

Use Foundry for reproducible deploys. Deploy to an L2 (recommended) and optionally checkpoint Merkle roots to L1.

Install + initialize

# Install Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup

# New repo
forge init crsp-nrc-eth
cd crsp-nrc-eth

Add contracts

Save NRCRegistry.sol under src/. (Use the minimal registry you already have, or your audited variant.)

Deploy script (example)

// script/Deploy.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Script.sol";
import {NRCRegistry} from "../src/NRCRegistry.sol";

contract Deploy is Script {
  function run() external returns (NRCRegistry reg) {
    vm.startBroadcast();
    reg = new NRCRegistry();
    vm.stopBroadcast();
  }
}

Deploy command

# Set your RPC + private key (use a deployer key with minimal privileges)
export RPC_URL="https://YOUR_L2_RPC"
export PRIVATE_KEY="0x..."

forge script script/Deploy.s.sol:Deploy \
  --rpc-url "$RPC_URL" \
  --private-key "$PRIVATE_KEY" \
  --broadcast \
  --verify
Operational best practice: set the instance controller to a Safe multisig (Sutter governance board), not a single EOA.

3) Register a Sutter NRC Template + Create an Instance

Template = policy contract hash. Instance = binding hash + controller + state machine. Everything else stays off-chain.

Off-chain artifacts (examples)

{
  "template_doc_uri": "uc://sutter.governance.nrc_templates/template_id=1001",
  "template_hash": "0xTEMPLATE_HASH...",
  "version": 1,

  "instance_id": "SUTTER_CARDIOLOGY_CDS_001",
  "binding_uri": "uc://sutter.governance.nrc_bindings/binding_id=...",
  "binding_hash": "0xBINDING_HASH...",
  "controller": "0xSAFE_MULTISIG_ADDRESS..."
}

On-chain actions

// Pseudocode calls (via Safe / controller):
registerTemplate(templateId=1001, templateHash=0x..., version=1)

instanceKey = keccak256("SUTTER_CARDIOLOGY_CDS_001")
createInstance(instanceKey, templateId=1001, controller=0xSAFE...)

bindInstance(instanceKey, bindingHash=0xBINDING_HASH)
Binding hash should include: domain (CARDIOLOGY), PHI classification (HIPAA), accountable roles, system boundaries (EHR read-only), allowed outputs, escalation/revocation rules.

4) Issue an Entitlement Token (optional “governance receipt”)

If you want a first-class entitlement artifact, mint a token that represents “an entitlement existed under NRC X”. This token is not a financial asset — it is a non-transferable compliance receipt.

Minimal ERC-1155 pattern (concept)

// EntitlementToken.sol (conceptual)
// - Mint only by controller (Safe) after ENTITLE
// - Non-transferable: disable safeTransferFrom / set approval rules
// - Token ID can be derived from instanceKey + entitlementHash

Receipt token semantics

  • tokenId = keccak256(instanceKey, entitlementHash)
  • metadata URI points to off-chain entitlement JSON (governed access)
  • mint occurs only when ENTITLE is approved
  • revoke/burn when defeated / superseded
Recommended default: emit events only (cheaper, simpler). Use tokens only if you need object-level lifecycle management.

5) AER loop: on-chain anchoring as event transactions

The AER loop runs off-chain (policy + evidence + human accountability). Ethereum receives event receipts: ASK, EVIDENCE, ENTITLE, RELEASE.

Canonical event header (hash this)

{
  "nrc_instance_id": "SUTTER_CARDIOLOGY_CDS_001",
  "event_type": "ENTITLE",
  "payload_hash": "0xENTITLEMENT_JSON_HASH...",
  "policy_hash": "0xTEMPLATE_HASH...",
  "binding_hash": "0xBINDING_HASH...",
  "actor_role": "ComplianceOfficer",
  "signers": ["0xSigner1","0xSigner2"],
  "prev_event_hash": "0xPREV...",
  "occurred_at": "2026-02-06T14:12:09Z"
}

On-chain write (appendEvent)

// via Safe multisig (controller)
appendEvent(
  instanceKey,
  "ENTITLE",
  eventHash,      // keccak256(canonical(header))
  payloadHash,    // hash(entitlement.json)
  newState=ENTITLED
)

Event ordering and revocation

  • prev_event_hash creates a tamper-evident chain of governance decisions
  • Use REVOKED state + new event to defeat/supersede a prior entitlement
  • Off-chain systems should treat RELEASE as valid only if the on-chain receipt exists
Important: only the controller (Safe) should be able to ENTITLE or RELEASE. ASK/EVIDENCE can be optional anchoring events, but ENTITLE/RELEASE are governance-critical.

6) Diagram-first explainer (AER ↔ Ethereum receipts)

Paste this Mermaid into your docs site / GitHub README if you want a fast executive view.

flowchart LR
  U[User / Clinician] --> A[CRSP ASK]
  A --> EV[Evidence Vault (UC / Encrypted Store)]
  EV --> E[CRSP EVIDENCE]
  E --> H[Human Review & Roles]
  H --> T[CRSP ENTITLE]
  T --> R[CRSP RELEASE]

  subgraph ETH[Ethereum / L2]
    REG[NRCRegistry.sol]
    REG -->|emit| LOGS[(NRCEvent logs)]
  end

  A -. optional receipt .-> REG
  E -. optional receipt .-> REG
  T == required receipt ==> REG
  R == required receipt ==> REG

  LOGS --> AUD[Auditor / Regulator Verifier]
  EV --> AUD

7) What “verification” looks like (auditor path)

  1. Auditor reads on-chain NRCEvent logs for instanceKey
  2. Auditor fetches the off-chain artifact (authorized access) referenced by the org’s systems
  3. Auditor recomputes payloadHash and confirms it matches the chain
  4. Auditor recomputes eventHash from the canonical event header
  5. Auditor verifies Safe multisig approvals for ENTITLE/RELEASE transactions
This yields court-grade answers to: who approved, under which policy, what exact artifact, and when it became immutable.

8) Production hardening checklist

  • Canonicalization: define a single canonical JSON serializer + stable key ordering
  • Key custody: all governance writes go through Safe; use role-based signer policies
  • Off-chain access: use KMS + UC ACLs; store only pointers + hashes on-chain
  • Batching: Merkle-batch high-volume ASK/EVIDENCE events; anchor roots periodically
  • Revocation: explicit REVOKED events + supersession links in entitlement JSON
  • Monitoring: alert when a release happens without the corresponding on-chain receipt