Files
CurrenciCombo/docs/Compliance_Integration_Spec.md

14 KiB

Compliance Integration Specification

Overview

This document specifies compliance integration requirements for the ISO-20022 Combo Flow system, including LEI/DID/KYC/AML injection into ISO messages, compliance engine API contract, real-time status checks, identity assertion format, and audit trail requirements for hybrid workflows.


1. Compliance Requirements

Required Compliance Attributes

Attribute Required For Description Format
LEI All workflows Legal Entity Identifier 20-character alphanumeric (e.g., 5493000IBP32UQZ0KL24)
DID Notarized workflows Decentralized Identifier W3C DID format (e.g., did:web:example.com:user:123)
KYC Fiat/DTL steps Know Your Customer verification Level 1-3 (Level 2+ for fiat)
AML Payments > threshold Anti-Money Laundering check Pass/Fail with risk level

Compliance Levels by Workflow Type

DeFi-Only Workflows

  • LEI: Optional (recommended)
  • DID: Optional
  • KYC: Not required
  • AML: Not required

Hybrid Workflows (DeFi + Fiat/DTL)

  • LEI: Required
  • DID: Required for notarization
  • KYC: Level 2+ required
  • AML: Required for payments > 10,000 EUR

Fiat-Only Workflows

  • LEI: Required
  • DID: Required
  • KYC: Level 2+ required
  • AML: Required for all payments

2. Compliance Engine API Contract

Interface: IComplianceEngine

interface IComplianceEngine {
  /**
   * Check compliance status for a user
   */
  getComplianceStatus(userId: string): Promise<ComplianceStatus>;

  /**
   * Validate compliance for a workflow
   */
  validateWorkflowCompliance(workflow: Workflow): Promise<ComplianceCheckResult>;

  /**
   * Run KYC verification
   */
  verifyKYC(userId: string, level: number): Promise<KYCResult>;

  /**
   * Run AML screening
   */
  screenAML(userId: string, amount: number, currency: string): Promise<AMLResult>;

  /**
   * Register LEI for user
   */
  registerLEI(userId: string, lei: string): Promise<boolean>;

  /**
   * Register DID for user
   */
  registerDID(userId: string, did: string): Promise<boolean>;
}

Compliance Status Response

interface ComplianceStatus {
  userId: string;
  lei: string | null;
  did: string | null;
  kyc: {
    level: number;
    provider: string;
    verified: boolean;
    expiresAt: string | null;
  };
  aml: {
    passed: boolean;
    provider: string;
    lastCheck: string;
    riskLevel: 'LOW' | 'MEDIUM' | 'HIGH';
  };
  valid: boolean;
}

Workflow Compliance Check

interface ComplianceCheckResult {
  valid: boolean;
  required: string[]; // ['LEI', 'DID', 'KYC', 'AML']
  missing: string[];
  warnings: string[];
}

3. LEI/DID/KYC/AML Injection into ISO Messages

ISO-20022 Message Structure with Compliance

pacs.008 (Payment Instruction) Example

<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10">
  <FIToFICstmrCdtTrf>
    <GrpHdr>
      <MsgId>MSG-2025-01-15-001</MsgId>
      <CreDtTm>2025-01-15T10:30:00Z</CreDtTm>
      <NbOfTxs>1</NbOfTxs>
      <CtrlSum>78000.00</CtrlSum>
      <InitgPty>
        <Nm>Example Corp Ltd.</Nm>
        <Id>
          <OrgId>
            <Othr>
              <Id>5493000IBP32UQZ0KL24</Id>
              <SchmeNm>
                <Cd>LEI</Cd>
              </SchmeNm>
            </Othr>
          </OrgId>
        </Id>
      </InitgPty>
    </GrpHdr>
    <CdtTrfTxInf>
      <PmtId>
        <EndToEndId>PLAN-12345678-ABCD-EFGH</EndToEndId>
        <TxId>TX-2025-01-15-001</TxId>
      </PmtId>
      <PmtTpInf>
        <SvcLvl>
          <Cd>SEPA</Cd>
        </SvcLvl>
        <LclInstrm>
          <Cd>INST</Cd>
        </LclInstrm>
      </PmtTpInf>
      <IntrBkSttlmAmt Ccy="EUR">78000.00</IntrBkSttlmAmt>
      <InstgAgt>
        <FinInstnId>
          <BICFI>BANKDEFFXXX</BICFI>
        </FinInstnId>
      </InstgAgt>
      <InstdAgt>
        <FinInstnId>
          <BICFI>BENEFRPPXXX</BICFI>
        </FinInstnId>
      </InstdAgt>
      <Dbtr>
        <Nm>Example Corp Ltd.</Nm>
        <Id>
          <OrgId>
            <Othr>
              <Id>5493000IBP32UQZ0KL24</Id>
              <SchmeNm>
                <Cd>LEI</Cd>
              </SchmeNm>
            </Othr>
          </OrgId>
        </Id>
        <CtctDtls>
          <Email>compliance@example.com</Email>
        </CtctDtls>
      </Dbtr>
      <DbtrAcct>
        <Id>
          <IBAN>DE89370400440532013000</IBAN>
        </Id>
      </DbtrAcct>
      <Cdtr>
        <Nm>Beneficiary Corp</Nm>
        <PstlAdr>
          <StrtNm>Main Street</StrtNm>
          <BldgNb>123</BldgNb>
          <PstCd>12345</PstCd>
          <TwnNm>Berlin</TwnNm>
          <Ctry>DE</Ctry>
        </PstlAdr>
      </Cdtr>
      <CdtrAcct>
        <Id>
          <IBAN>DE89370400440532013001</IBAN>
        </Id>
      </CdtrAcct>
      <RmtInf>
        <Ustrd>Plan ID: PLAN-12345678-ABCD-EFGH</Ustrd>
        <Strd>
          <RfrdDocInf>
            <Tp>
              <CdOrPrtry>
                <Cd>CINV</Cd>
              </CdOrPrtry>
            </Tp>
            <Nb>PLAN-12345678-ABCD-EFGH</Nb>
            <RltdDt>2025-01-15</RltdDt>
          </RfrdDocInf>
          <RfrdDocAmt>
            <DuePyblAmt Ccy="EUR">78000.00</DuePyblAmt>
          </RfrdDocAmt>
        </Strd>
      </RmtInf>
      <SplmtryData>
        <PlcAndNm>ComplianceData</PlcAndNm>
        <Envlp>
          <ComplianceData xmlns="urn:example:compliance:xsd:1.0">
            <PlanId>PLAN-12345678-ABCD-EFGH</PlanId>
            <LEI>5493000IBP32UQZ0KL24</LEI>
            <DID>did:web:example.com:user:123</DID>
            <KYC>
              <Level>2</Level>
              <Provider>Onfido</Provider>
              <Verified>true</Verified>
              <ExpiresAt>2026-12-31T23:59:59Z</ExpiresAt>
            </KYC>
            <AML>
              <Passed>true</Passed>
              <Provider>Chainalysis</Provider>
              <LastCheck>2025-01-15T09:00:00Z</LastCheck>
              <RiskLevel>LOW</RiskLevel>
            </AML>
            <DigitalSignature>
              <Algorithm>ECDSA</Algorithm>
              <Value>0x1234567890abcdef...</Value>
            </DigitalSignature>
          </ComplianceData>
        </Envlp>
      </SplmtryData>
    </CdtTrfTxInf>
  </FIToFICstmrCdtTrf>
</Document>

Key Compliance Injection Points

  1. Header (GrpHdr.InitgPty): LEI in Id.OrgId.Othr.Id with SchmeNm.Cd = "LEI"
  2. Debtor (Dbtr): LEI in Id.OrgId.Othr.Id
  3. Supplementary Data (SplmtryData): Full compliance data (LEI, DID, KYC, AML, signature)

4. Real-Time Status Checks

API Endpoint: GET /api/compliance/status

// Request
GET /api/compliance/status?userId=user123

// Response
{
  "userId": "user123",
  "lei": "5493000IBP32UQZ0KL24",
  "did": "did:web:example.com:user:123",
  "kyc": {
    "level": 2,
    "provider": "Onfido",
    "verified": true,
    "expiresAt": "2026-12-31T23:59:59Z"
  },
  "aml": {
    "passed": true,
    "provider": "Chainalysis",
    "lastCheck": "2025-01-15T09:00:00Z",
    "riskLevel": "LOW"
  },
  "valid": true
}

Workflow Compliance Check: POST /api/compliance/check

// Request
POST /api/compliance/check
{
  "steps": [
    { "type": "borrow", "asset": "CBDC_USD", "amount": 100000 },
    { "type": "swap", "from": "CBDC_USD", "to": "CBDC_EUR", "amount": 100000 },
    { "type": "pay", "asset": "EUR", "amount": 78000, "beneficiary": { "IBAN": "DE89..." } }
  ]
}

// Response
{
  "valid": true,
  "required": ["LEI", "DID", "KYC", "AML"],
  "missing": [],
  "warnings": []
}

Frontend Integration

// Real-time compliance badge in UI
const ComplianceBadge = () => {
  const { data: compliance } = useQuery(['compliance'], () => 
    api.getComplianceStatus()
  );

  return (
    <div className="flex gap-2">
      {compliance?.lei && <Badge> LEI</Badge>}
      {compliance?.did && <Badge> DID</Badge>}
      {compliance?.kyc?.verified && <Badge> KYC</Badge>}
      {compliance?.aml?.passed && <Badge> AML</Badge>}
    </div>
  );
};

5. Identity Assertion Format

W3C Verifiable Credential Format

{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://www.w3.org/2018/credentials/examples/v1"
  ],
  "id": "https://example.com/credentials/123",
  "type": ["VerifiableCredential", "ComplianceCredential"],
  "issuer": {
    "id": "did:web:example.com:issuer",
    "name": "Compliance Authority"
  },
  "issuanceDate": "2025-01-15T10:00:00Z",
  "credentialSubject": {
    "id": "did:web:example.com:user:123",
    "lei": "5493000IBP32UQZ0KL24",
    "kyc": {
      "level": 2,
      "provider": "Onfido",
      "verified": true,
      "expiresAt": "2026-12-31T23:59:59Z"
    },
    "aml": {
      "passed": true,
      "provider": "Chainalysis",
      "lastCheck": "2025-01-15T09:00:00Z",
      "riskLevel": "LOW"
    }
  },
  "proof": {
    "type": "Ed25519Signature2020",
    "created": "2025-01-15T10:00:00Z",
    "verificationMethod": "did:web:example.com:issuer#key-1",
    "proofPurpose": "assertionMethod",
    "proofValue": "z5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5V"
  }
}

Entra Verified ID Integration

// Request verified credential from Entra
const requestCredential = async (userId: string) => {
  const response = await fetch('https://verifiedid.did.msidentity.com/v1.0/verifiableCredentials/request', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      credentialType: 'ComplianceCredential',
      claims: {
        lei: user.lei,
        kycLevel: user.kyc.level
      }
    })
  });

  return response.json();
};

6. Audit Trail Requirements

Audit Log Structure

interface AuditLog {
  planId: string;
  timestamp: string;
  event: 'PLAN_CREATED' | 'COMPLIANCE_CHECKED' | 'EXECUTION_STARTED' | 'EXECUTION_COMPLETED' | 'EXECUTION_FAILED';
  userId: string;
  compliance: {
    lei: string;
    did: string;
    kyc: KYCStatus;
    aml: AMLStatus;
  };
  metadata: {
    steps: PlanStep[];
    dltTxHash?: string;
    isoMessageId?: string;
    notaryProof?: string;
  };
}

Audit Trail Storage

  1. On-Chain (NotaryRegistry): Immutable proof hashes
  2. Off-Chain (Database): Full audit logs with compliance data
  3. ISO Messages: Compliance data embedded in messages

Compliance Audit Report

interface ComplianceAuditReport {
  planId: string;
  executionDate: string;
  user: {
    userId: string;
    lei: string;
    did: string;
  };
  compliance: {
    kyc: {
      level: number;
      provider: string;
      verified: boolean;
      expiresAt: string;
    };
    aml: {
      passed: boolean;
      provider: string;
      lastCheck: string;
      riskLevel: string;
    };
  };
  workflow: {
    steps: PlanStep[];
    totalAmount: number;
    currency: string;
  };
  receipts: {
    dltTxHash: string;
    isoMessageId: string;
    notaryProof: string;
  };
}

7. Compliance Workflow Integration

Step 1: User Registration

// User registers LEI and DID
await complianceEngine.registerLEI(userId, lei);
await complianceEngine.registerDID(userId, did);

Step 2: KYC Verification

// Run KYC verification (Level 2+ for fiat workflows)
const kycResult = await complianceEngine.verifyKYC(userId, 2);
if (!kycResult.verified) {
  throw new Error('KYC verification failed');
}

Step 3: AML Screening

// Run AML screening for payments > threshold
const amlResult = await complianceEngine.screenAML(userId, amount, 'EUR');
if (!amlResult.passed) {
  throw new Error('AML screening failed');
}

Step 4: Workflow Compliance Check

// Validate compliance before execution
const complianceCheck = await complianceEngine.validateWorkflowCompliance(workflow);
if (!complianceCheck.valid) {
  throw new Error(`Missing compliance: ${complianceCheck.missing.join(', ')}`);
}

Step 5: ISO Message Generation

// Generate ISO message with compliance data
const isoMessage = generateISO20022Message(plan, {
  lei: complianceStatus.lei,
  did: complianceStatus.did,
  kyc: complianceStatus.kyc,
  aml: complianceStatus.aml,
  signature: planSignature
});

8. Error Handling

Compliance Validation Failures

// Compliance check fails
if (!complianceCheck.valid) {
  return {
    error: 'COMPLIANCE_REQUIRED',
    message: `Missing compliance attributes: ${complianceCheck.missing.join(', ')}`,
    missing: complianceCheck.missing,
    required: complianceCheck.required
  };
}

KYC Expiration Warning

// Check if KYC is expiring soon
if (complianceStatus.kyc.expiresAt) {
  const expiresAt = new Date(complianceStatus.kyc.expiresAt);
  const daysUntilExpiry = (expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24);
  
  if (daysUntilExpiry < 30) {
    return {
      warning: 'KYC_EXPIRING_SOON',
      message: `KYC expires in ${daysUntilExpiry} days`,
      expiresAt: complianceStatus.kyc.expiresAt
    };
  }
}

9. Testing Requirements

Unit Tests

describe('ComplianceEngine', () => {
  it('should validate LEI format', () => {
    expect(validateLEI('5493000IBP32UQZ0KL24')).toBe(true);
    expect(validateLEI('invalid')).toBe(false);
  });

  it('should check workflow compliance', async () => {
    const result = await complianceEngine.validateWorkflowCompliance(workflow);
    expect(result.valid).toBe(true);
    expect(result.missing).toEqual([]);
  });
});

Integration Tests

describe('ISO Message Generation', () => {
  it('should inject compliance data into pacs.008', () => {
    const message = generateISO20022Message(plan, complianceData);
    expect(message).toContain('<LEI>5493000IBP32UQZ0KL24</LEI>');
    expect(message).toContain('<DID>did:web:example.com:user:123</DID>');
  });
});

Document Version: 1.0
Last Updated: 2025-01-15
Author: Compliance Team