Example Walkthrough

Complete step-by-step example of integrating 4Quays into a banking application

This walkthrough demonstrates integrating 4Quays into a banking application that sends transfers to multiple banks with different cryptographic requirements.

Scenario

Your application needs to integrate with three financial institutions:

ServiceRequirement
Bank AAES-256-GCM encryption with RSA key wrapping
Bank BAES-256-GCM encryption with ML-KEM-768 (post-quantum)
Bank CPlaintext over TLS only

Before 4Quays

Your current code might look like this:

// banking-service.js (before 4Quays)
import { loadKey, encryptWithRSA, encryptWithMLKEM } from './crypto';

async function sendTransfer(accountId, transferData) {
  const account = await getAccount(accountId);

  if (account.bank === 'bank-a') {
    const key = loadKey('bank-a-public.pem');
    const encrypted = encryptWithRSA(transferData, key);
    return await bankAApi.submitTransfer(encrypted);
  }

  if (account.bank === 'bank-b') {
    const key = loadKey('bank-b-pqc-public.pem');
    const encrypted = encryptWithMLKEM(transferData, key);
    return await bankBApi.submitTransfer(encrypted);
  }

  if (account.bank === 'bank-c') {
    // Plaintext over TLS
    return await bankCApi.submitTransfer(transferData);
  }
}

Problems with this approach:

  • Crypto logic embedded in application code
  • Key management scattered across files
  • Each algorithm change requires code changes
  • No centralized audit trail
  • Developers must understand cryptographic details

Step 1: Platform Setup

In the 4Quays dashboard:

Register Services

  1. Your Application (source service)

    • Name: "Banking Service"
    • Type: Source
  2. Bank A (destination service)

    • Name: "Bank A"
    • Type: Destination
    • Import their RSA public key
  3. Bank B (destination service)

    • Name: "Bank B"
    • Type: Destination
    • Import their ML-KEM public key

Create Policies

  1. Bank A Transfer Policy

    • Policy Number: BANK-A-TRANSFER
    • Algorithm: AES-256-GCM + RSA-2048
    • Source: Banking Service
    • Destination: Bank A
  2. Bank B Transfer Policy

    • Policy Number: BANK-B-TRANSFER
    • Algorithm: AES-256-GCM + ML-KEM-768
    • Source: Banking Service
    • Destination: Bank B

Generate API Key

Create an API key for your application. Store it securely.

Step 2: Install SDK

npm install @4quays/sdk

Step 3: Initialize SDK

// lib/fourq.js
import { FourQ } from '@4quays/sdk';

export const fourq = new FourQ({
  endpoint: process.env.FOURQ_ENDPOINT,
  apiKey: process.env.FOURQ_API_KEY,
});

Step 4: Create Policy Mapping

// config/policies.js
export const bankPolicies = {
  'bank-a': 'BANK-A-TRANSFER',
  'bank-b': 'BANK-B-TRANSFER',
  'bank-c': null,  // No policy = plaintext over TLS
};

Step 5: Update Transfer Logic

// banking-service.js (with 4Quays)
import { fourq } from './lib/fourq';
import { bankPolicies } from './config/policies';

async function sendTransfer(accountId, transferData) {
  const account = await getAccount(accountId);
  const policy = bankPolicies[account.bank];

  // Build the transfer payload
  const payload = {
    transferId: generateId(),
    sourceAccount: account.number,
    destinationIban: transferData.destination,
    amount: transferData.amount,
    currency: 'CAD',
    memo: transferData.memo,
  };

  // Protect if policy exists, otherwise send plaintext
  const protectedPayload = policy
    ? await fourq.protect(payload, policy)
    : payload;

  // Get the appropriate bank API
  const bankApi = getBankApi(account.bank);

  // Send to bank - same code for all banks
  return await bankApi.submitTransfer({
    encryption: protectedPayload,
    timestamp: new Date().toISOString(),
  });
}

Step 6: Handle Responses

If banks return encrypted responses:

async function getStatement(accountId) {
  const account = await getAccount(accountId);
  const bankApi = getBankApi(account.bank);

  const response = await bankApi.getStatement(account.number);

  // Unprotect if encrypted
  const policy = bankPolicies[account.bank];
  if (policy && response.encryption) {
    return await fourq.unprotect(response.encryption, policy);
  }

  return response.data;
}

What Changed

BeforeAfter
Multiple crypto libraries importedSingle SDK import
Key files in applicationKeys in 4Quays platform
Algorithm logic in codeAlgorithm in policy
Per-bank crypto codeSingle protect() call
No audit trailFull audit in 4Quays

The Resulting Code

The complete updated service:

// banking-service.js
import { fourq } from './lib/fourq';
import { bankPolicies } from './config/policies';

export async function sendTransfer(accountId, transferData) {
  const account = await getAccount(accountId);
  const policy = bankPolicies[account.bank];

  const payload = {
    transferId: generateId(),
    sourceAccount: account.number,
    destinationIban: transferData.destination,
    amount: transferData.amount,
    currency: 'CAD',
    memo: transferData.memo,
  };

  const protectedPayload = policy
    ? await fourq.protect(payload, policy)
    : payload;

  const bankApi = getBankApi(account.bank);

  return await bankApi.submitTransfer({
    encryption: protectedPayload,
    timestamp: new Date().toISOString(),
  });
}

export async function getStatement(accountId) {
  const account = await getAccount(accountId);
  const bankApi = getBankApi(account.bank);

  const response = await bankApi.getStatement(account.number);

  const policy = bankPolicies[account.bank];
  if (policy && response.encryption) {
    return await fourq.unprotect(response.encryption, policy);
  }

  return response.data;
}

Algorithm Migration Example

When Bank A announces they're migrating to post-quantum:

Before 4Quays: Developers update crypto code, test, deploy.

With 4Quays:

  1. Security team updates the BANK-A-TRANSFER policy
  2. Changes algorithm from RSA to ML-KEM-768
  3. Policy version is incremented automatically
  4. Next protect() call uses new algorithm
  5. No code changes required

Development Testing

During development without 4Quays running:

# .env.development
# FOURQ_ENDPOINT= (not set)

The code works the same, but Bank A and B receive plaintext (and may reject). This is expected — you're testing business logic, not crypto.

Production Deployment

For production:

# .env.production
FOURQ_ENDPOINT=https://api.4quays.com
FOURQ_API_KEY=sk_live_xxxxx

Same code, different config. Bank A and B now receive properly encrypted payloads.

What's Next