Loading...
private.me Docs
Get Airgapexchange
PRIVATE.ME · Technical White Paper

Airgapexchange: Zero-Communication Key Exchange

Air-gapped X25519 + Ed25519 key exchange via QR-printable XorIDA threshold shares delivered by independent physical couriers. Zero electronic communication between parties. Mathematically impossible to intercept remotely. Bootstrap secure channels in hostile environments where all electronic paths are compromised, monitored, or unavailable.

v0.1.0 Zero electronic risk K-of-N threshold QR-printable Base45 + Xformat Dual ESM/CJS
Section 01

Executive Summary

Airgapexchange solves the bootstrapping problem for secure communications in environments where electronic channels cannot be trusted: diplomatic missions, military deployments, intelligence operations, critical infrastructure, and high-value negotiations.

Traditional key exchange protocols require an electronic channel. Diffie-Hellman, X25519 ECDH, ML-KEM key encapsulation — all assume you can send bytes over the network. When the network itself is the threat, these protocols fail.

Airgapexchange uses XorIDA threshold sharing to split X25519 public keys and Ed25519 signing keys into K-of-N QR-printable shares. Each share is Base45-encoded and wrapped in an Xformat envelope for tamper detection. Print each share as a physical QR code and hand it to a different courier. The couriers travel independently — different routes, different timing, different modes of transport.

The recipient scans any K shares, reconstructs the sender's public keys, performs X25519 ECDH to derive a shared secret, and establishes a secure channel. An attacker who compromises fewer than K couriers learns nothing about the keys — not computationally hard to break, but mathematically impossible.

Once the shared secret is established, parties can communicate via encrypted channels using the derived key. Airgapexchange is the secure bootstrap. The ongoing conversation happens via Xlink, email with XorIDA split-channel, or any authenticated encryption scheme.

Section 02

The Problem

Key exchange protocols require electronic communication channels. In hostile environments, all electronic channels may be compromised, monitored, or unavailable. There is no standard protocol for bootstrapping secure communications when the network itself is the adversary.

Why Traditional Approaches Fail

Diffie-Hellman requires an electronic channel. Classic DH, ECDH (X25519), and post-quantum KEMs (ML-KEM-768) all assume you can exchange public keys over a network. If nation-state adversaries control the network, they intercept, manipulate, or block the exchange. A man-in-the-middle substitutes their own keys. You establish a "secure" channel with the attacker, not your intended counterparty.

Pre-shared keys don't scale. Manually distributing symmetric keys to every pair of communicating parties is logistically infeasible. A single stolen USB drive or photographed key sheet exposes the entire communication channel. No forward secrecy, no fault tolerance.

Trust-on-first-use (TOFU) fails under active attack. SSH, Signal, and WhatsApp use TOFU: "Trust this key the first time you see it." If the first exchange is compromised, you trust the attacker's key forever. No out-of-band verification, no second chance.

Single courier is a single point of failure. Handing one courier a full key creates a catastrophic risk. Coercion, bribery, interception, or a simple traffic accident means total compromise. No redundancy, no threshold protection.

HOSTILE ENVIRONMENT THREAT MODEL
In diplomatic, military, and intelligence contexts, assume all electronic channels are monitored. Network operators, ISPs, certificate authorities, DNS resolvers, and endpoint devices may be compromised or coerced. Physical interception of couriers is feasible. Traditional cryptographic protocols that assume an unmonitored initialization phase do not apply.

Real-World Scenarios

Embassy-to-HQ secure channel. A diplomatic mission in a hostile country needs to establish encrypted communications with the home office. The local internet is state-controlled. Certificate authorities cannot be trusted. A compromised VPN endpoint means full traffic visibility to adversaries. Electronic key exchange is not an option.

Military forward operating base. A field unit deploys to a contested region with no secure communications infrastructure. Satellite links may be jammed or intercepted. Radio frequencies are monitored. The unit needs to establish command-and-control channels without electronic exposure during initialization.

Intelligence dead drop alternative. Traditional physical dead drops have a single point of failure: one compromised location exposes the entire operation. Threshold-protected key exchange distributes the risk across multiple independent couriers, each carrying insufficient information to compromise the channel.

Critical infrastructure network segmentation. A nuclear facility, power grid control center, or water treatment plant requires cryptographic isolation between operational technology (OT) networks and external systems. Air-gapped key exchange establishes secure channels without any electronic path that could be exploited during the initialization phase.

Section 03

Solution Architecture

Airgapexchange combines XorIDA threshold secret sharing, QR code transport, and X25519 ECDH to enable secure key exchange with zero electronic communication during the bootstrap phase.

Core Components

Xformat Envelopes
Binary
26-byte header with IDA5 magic
Product type: XMEET (0x19)
UUID for exchange grouping
K/N parameters embedded
Base45 Encoding
QR-optimized
RFC 9285 alphanumeric mode
~10% more efficient than Base64
QR alphanumeric capacity
Uppercase ASCII charset
X25519 ECDH
Forward Secrecy
32-byte public key exchange
Web Crypto API derivation
AES-256-GCM key output
Optional: defer ECDH until later

Protocol Overview

Alice wants to establish a secure channel with Bob. They cannot trust any electronic communication during the bootstrap phase. Alice generates an X25519 keypair and an Ed25519 signing keypair. She keeps the private keys. She takes the two public keys (32 bytes each), concatenates them with her DID, and uses prepareExchange() to split the payload into K-of-N QR shares.

Each share is a Base45-encoded Xformat envelope containing one XorIDA share of the public key bundle. Alice prints each share as a QR code on separate pieces of paper. She hands each QR code to a different courier: Share 1 via diplomatic pouch, Share 2 via trusted traveler, Share 3 via commercial courier service. The couriers travel independently.

Bob receives the QR codes as they arrive. When he has scanned K shares, he calls completeExchange(qrShares, bobPrivateKey). The function reconstructs Alice's public keys, verifies HMAC integrity, extracts her DID, and performs X25519 ECDH using Bob's private key and Alice's reconstructed public key. The output is a 32-byte shared secret.

Alice and Bob now share a cryptographic secret that was never transmitted electronically. They can use this shared secret as an AES-256-GCM key, an HMAC key, or input to HKDF for deriving session keys. The secure channel is bootstrapped.

BIDIRECTIONAL EXCHANGE
For a full bidirectional secure channel, both parties execute the protocol: Alice sends K-of-N shares of her public keys to Bob via couriers, and Bob sends K-of-N shares of his public keys to Alice via independent couriers. Each party derives the same shared secret via ECDH. Once both sides have the shared secret, they can communicate via Xlink envelopes, encrypted email, or any authenticated encryption scheme.
Section 04

Real-World Use Cases

Six scenarios where electronic key exchange is impossible or unacceptable.

🏛
Diplomacy
Embassy-to-HQ Secure Channel

Diplomatic missions in hostile countries cannot trust local network infrastructure. Couriers carry QR shares via diplomatic pouch (share 1), trusted attaché (share 2), and commercial flight (share 3). 2-of-3 threshold ensures channel bootstrap even if one courier is delayed or compromised.

2-of-3 threshold, diplomatic pouch delivery
🛡
Defense
Forward Operating Base

Military units deploying to contested regions with no secure communications infrastructure. Shares delivered via different transport modes: air drop (share 1), ground convoy (share 2), supply helicopter (share 3). Couriers never carry enough information to compromise the channel.

3-of-5 threshold, multi-transport delivery
🔍
Intelligence
Source Communication

Intelligence officers and confidential sources establish secure channels without electronic exposure. Shares delivered via dead drop (share 1), trusted intermediary (share 2), postal service (share 3). Threshold protection means a single compromised drop reveals nothing.

2-of-3 threshold, dead drop delivery
Critical Infrastructure
OT Network Segmentation

Nuclear facilities, power grids, and water treatment plants require cryptographic isolation between operational technology networks and external systems. Air-gapped key exchange establishes secure channels without any electronic path exploitable during initialization.

2-of-2 threshold, hand delivery only
💼
M&A / Legal
Confidential Negotiations

High-stakes merger negotiations, legal settlements, and corporate transactions require cryptographically secure channels without exposing intent via monitored electronic communications. Couriers deliver shares via separate law firms, investment banks, and commercial services.

2-of-3 threshold, legal courier delivery
🛰
Space / Satellite
Ground Station Bootstrap

Satellite ground stations in multiple countries establish secure channels for telemetry and command data. Electronic key exchange exposes critical infrastructure to interception. Physical QR shares delivered via trusted personnel to each ground station. 3-of-5 threshold tolerates compromised stations.

3-of-5 threshold, international delivery
Section 05

Technical Architecture

Four-layer architecture: key generation, threshold splitting, envelope serialization, and QR transport.

Protocol Flow

Step 1: Key Generation (Sender)
Alice generates an X25519 keypair for key agreement and an Ed25519 keypair for signing. She exports the public keys as raw 32-byte Uint8Arrays. She constructs an ExchangeKeys object containing her X25519 public key, Ed25519 signing public key, and DID.

Key generation
const aliceKeys: ExchangeKeys = {
  publicKey: aliceX25519PublicKey,       // Uint8Array, 32 bytes
  signingPublicKey: aliceEd25519PubKey,  // Uint8Array, 32 bytes
  did: 'did:key:z6Mk...',
};

Step 2: Threshold Splitting (Sender)
Alice calls prepareExchange(aliceKeys, config) with a configuration specifying N total shares, K threshold, and optional courier count. The function serializes the public keys + DID into a binary payload, applies PKCS#7 padding, splits via XorIDA, generates HMAC-SHA256 for each share, wraps each share in an Xformat envelope with product type XMEET (0x19), and Base45-encodes the envelope for QR printing.

Prepare exchange
const config: ExchangeConfig = {
  n: 3,             // total shares
  k: 2,             // threshold to reconstruct
  courierCount: 3,  // independent couriers (>= n)
  label: 'HQ-to-Embassy key exchange 2026-Q1',
};

const result = await prepareExchange(aliceKeys, config);
if (!result.ok) throw new Error(result.error);

// Hand each share.qrData to a different courier as a printed QR code
for (const share of result.value.shares) {
  console.log(`Share ${share.shareId}: ${share.qrData}`);
}

Step 3: Physical Transport
Alice prints each share.qrData string as a QR code. She hands each printed QR code to a different courier. The couriers travel independently via different routes, different timing, different modes of transport. No electronic communication occurs between Alice and Bob during this phase.

Step 4: Share Collection (Recipient)
Bob scans arriving QR codes using any standard QR scanner (camera app, dedicated scanner, etc.). He collects the Base45-encoded strings in an array. He does not need all N shares — only K shares are required.

Step 5: Reconstruction and ECDH (Recipient)
Bob calls completeExchange(qrShares, bobX25519PrivateKey). The function Base45-decodes each QR string, deserializes the Xformat envelope, verifies the product type is XMEET, checks HMAC-SHA256 integrity, reconstructs the public key bundle via XorIDA, unpads PKCS#7, deserializes the payload, extracts Alice's X25519 public key and Ed25519 signing public key, and performs X25519 ECDH using Bob's private key and Alice's reconstructed public key to derive a 32-byte shared secret.

Complete exchange
const scannedQrCodes: string[] = [couriers[0].qrData, couriers[1].qrData];

const completed = await completeExchange(scannedQrCodes, bobX25519PrivateKey);
if (!completed.ok) throw new Error(completed.error);

console.log(completed.value.peerDid);       // Alice's DID
console.log(completed.value.sharedSecret);   // 32-byte X25519 ECDH shared secret

Step 6: Secure Channel Bootstrap
Alice and Bob now share a cryptographic secret. They can use this shared secret directly as an AES-256-GCM key, pass it to HKDF-SHA256 to derive multiple session keys, or use it as input to any key derivation function. The secure channel is established.

Security Model

Threshold Security: Fewer than K couriers cannot reconstruct either party's public key. XorIDA threshold sharing is information-theoretically secure — no amount of computational power can recover the key from K-1 shares. An attacker must compromise K or more couriers to learn anything about the keys.

HMAC Integrity: Each share includes an HMAC-SHA256 tag computed over the share data. The tag is verified before reconstruction. Tampered shares are detected and rejected immediately. A single corrupted or manipulated share fails HMAC verification and prevents reconstruction, forcing the attacker to deliver valid shares or be detected.

Courier Independence: Shares are delivered via independent couriers using different routes, different timing, and different modes of transport. A single compromised courier reveals nothing. A single delayed courier does not block the exchange (as long as K shares arrive). Courier independence is enforced by the sender — Alice chooses N >= K couriers and ensures they do not coordinate or communicate.

No Electronic Exposure: The public keys never transit an electronic channel during the bootstrap phase. An adversary monitoring all electronic communications learns zero bits about the keys. Network interception, TLS MITM, compromised CAs, DNS poisoning, and BGP hijacking are all irrelevant during the air-gapped exchange.

Forward Secrecy: Once the shared secret is established, parties can use ephemeral X25519 keypairs for each session (via Xlink or another ECDH-based protocol). Compromise of the air-gapped bootstrap keys does not compromise past or future sessions if ephemeral keys are used correctly.

PHYSICAL SECURITY ASSUMPTION
Airgapexchange assumes that fewer than K couriers will be compromised. If an adversary can intercept, coerce, or bribe K or more couriers, the exchange is compromised. Choose K and N based on your threat model. For high-threat environments, use 3-of-5 or 4-of-7 to tolerate multiple courier failures or compromises.
Section 06

Integration Guide

Install, generate keys, split shares, print QR codes, scan shares, reconstruct keys, derive shared secret.

Installation

npm install
npm install @private.me/airgapexchange

Sender Flow (Alice)

Prepare exchange
import { prepareExchange } from '@private.me/airgapexchange';
import type { ExchangeKeys, ExchangeConfig } from '@private.me/airgapexchange';

// 1. Generate X25519 + Ed25519 keypairs (Web Crypto API)
const x25519KeyPair = await crypto.subtle.generateKey(
  { name: 'ECDH', namedCurve: 'X25519' },
  true,
  ['deriveKey', 'deriveBits']
);

const ed25519KeyPair = await crypto.subtle.generateKey(
  { name: 'Ed25519' },
  true,
  ['sign', 'verify']
);

// 2. Export public keys as raw bytes
const x25519PubBytes = new Uint8Array(
  await crypto.subtle.exportKey('raw', x25519KeyPair.publicKey)
);
const ed25519PubBytes = new Uint8Array(
  await crypto.subtle.exportKey('raw', ed25519KeyPair.publicKey)
);

// 3. Prepare exchange configuration
const aliceKeys: ExchangeKeys = {
  publicKey: x25519PubBytes,
  signingPublicKey: ed25519PubBytes,
  did: 'did:key:z6Mk...',
};

const config: ExchangeConfig = {
  n: 3,
  k: 2,
  courierCount: 3,
  label: 'Alice-to-Bob 2026-Q1',
};

// 4. Split into QR shares
const result = await prepareExchange(aliceKeys, config);
if (!result.ok) {
  console.error('Exchange prep failed:', result.error);
  throw new Error(result.error);
}

// 5. Print each share as a QR code
result.value.shares.forEach((share, idx) => {
  console.log(`Share ${idx + 1} QR data:`, share.qrData);
  // Use a QR code library to generate printable QR images
  // Hand each printed QR to a different courier
});

Recipient Flow (Bob)

Complete exchange
import { completeExchange } from '@private.me/airgapexchange';

// 1. Scan QR codes from arriving couriers
const scannedQrCodes: string[] = [
  'QR1_BASE45_STRING...',  // from courier 1
  'QR2_BASE45_STRING...',  // from courier 2
];

// 2. Bob's X25519 private key (pre-generated)
const bobX25519PrivateKey: CryptoKey = ...; // from Web Crypto API

// 3. Reconstruct Alice's keys and derive shared secret
const completed = await completeExchange(scannedQrCodes, bobX25519PrivateKey);
if (!completed.ok) {
  console.error('Exchange completion failed:', completed.error);
  throw new Error(completed.error);
}

// 4. Use the shared secret
const { peerPublicKey, peerSigningPublicKey, peerDid, sharedSecret } = completed.value;

console.log('Peer DID:', peerDid);
console.log('Shared secret (32 bytes):', sharedSecret);

// sharedSecret can now be used for AES-256-GCM, HMAC, or HKDF input

Bidirectional Exchange

For a full bidirectional secure channel, both Alice and Bob execute the sender flow and the recipient flow. Alice sends QR shares of her public keys to Bob. Bob sends QR shares of his public keys to Alice. Each party scans K shares from the other party and derives the same shared secret via ECDH. Once both sides have the shared secret, they can encrypt messages using AES-256-GCM or establish Xlink agent-to-agent channels.

Section 07

Performance Benchmarks

Airgapexchange performance is dominated by physical courier transit time, not computation time. Cryptographic operations complete in milliseconds.

Computational Benchmarks

<5ms
Prepare 2-of-3 shares
<3ms
Reconstruct + ECDH
~150B
QR data per share
0
Network round trips

Key generation: X25519 + Ed25519 keypair generation via Web Crypto API takes ~2ms on modern hardware. This is a one-time operation per party.

Share preparation: Splitting 64 bytes (two 32-byte public keys) into 2-of-3 shares via XorIDA + HMAC + Xformat + Base45 encoding takes ~5ms. Most of this time is HMAC-SHA256 computation and Base45 encoding.

Share reconstruction: Decoding 2 QR shares, verifying HMAC, reconstructing via XorIDA, and performing X25519 ECDH takes ~3ms. HMAC verification dominates this operation.

QR code size: Each share is ~150 bytes of Base45-encoded data. This fits comfortably in a QR code Version 3 (29x29 modules) with error correction level M (15% damage tolerance). Printing and scanning are sub-second operations with modern devices.

Real-World Timing

Cryptographic operations are negligible compared to physical courier transit time. A typical air-gapped exchange takes hours to days depending on courier routes:

  • Local delivery (same city): 2-4 hours for all K couriers to arrive
  • International delivery (different countries): 24-72 hours depending on courier mode (air vs ground)
  • High-security delivery (diplomatic pouch): 1-7 days depending on routing and security protocols

The computational overhead is effectively zero relative to physical transit time. Choose courier count and threshold based on security requirements and acceptable delivery latency, not cryptographic performance.

Section 08

Honest Limitations

Airgapexchange is not a replacement for traditional key exchange protocols. It is a bootstrapping mechanism for hostile environments where electronic channels cannot be trusted.

What Airgapexchange Does NOT Solve

Courier compromise. If an adversary can intercept, coerce, or bribe K or more couriers, the exchange is compromised. Choose K and N based on your threat model. For high-threat environments, use 3-of-5 or 4-of-7 to tolerate multiple courier failures.

Bidirectional bootstrap latency. A full bidirectional secure channel requires both parties to send QR shares to each other. This doubles the courier transit time. If Alice needs 24 hours for her couriers to reach Bob, and Bob needs 24 hours for his couriers to reach Alice, the total bootstrap time is 48 hours (assuming parallel courier dispatch).

No authentication of couriers. Airgapexchange does not provide cryptographic proof that a courier is legitimate. The sender must choose trusted couriers and verify their identity through out-of-band means. A substituted courier carrying a valid-looking QR code can deliver a share that passes HMAC verification but belongs to an attacker's key exchange.

No protection against QR forgery. An attacker who compromises K couriers can substitute their own QR codes. HMAC verification only proves that the share matches the other shares in the group — it does not prove the shares represent the intended sender's keys. Combine airgapexchange with out-of-band key fingerprint verification (e.g., Alice reads her Ed25519 public key fingerprint to Bob over a trusted voice channel).

Ongoing communication requires a separate protocol. Airgapexchange establishes a shared secret. It does not provide message encryption, replay protection, or forward secrecy for ongoing communications. Use Xlink, Signal Protocol, or another authenticated encryption scheme for actual message exchange after the bootstrap.

No revocation or key rotation. Once the shared secret is established, there is no built-in mechanism to revoke or rotate keys. If a key is compromised, parties must execute a new air-gapped exchange to establish a fresh shared secret. For long-lived channels, use ephemeral X25519 keypairs for each session (via Xlink) to provide forward secrecy independent of the bootstrap keys.

WHEN TO USE AIRGAPEXCHANGE
Use airgapexchange when:
• All electronic channels are monitored or compromised
• Physical courier delivery is feasible and acceptable
• Bootstrap latency (hours to days) is acceptable
• Threshold protection against courier compromise is required

Do NOT use airgapexchange when:
• Electronic key exchange is trusted (use X25519 ECDH directly)
• Real-time bootstrap is required (use TOFU or pre-shared keys)
• Physical courier delivery is not feasible
• Single courier is acceptable (use encrypted USB or other transport)
Advanced Topics

For Integration Engineers

Error handling, API reference, deployment patterns for production air-gapped key exchange systems.

Appendix A1

Error Handling

All errors use colon-separated sub-codes for detailed diagnostics. Base codes are stable; sub-codes provide context.

Error Categories

Category Example Codes When
Configuration INVALID_CONFIG:N_TOO_SMALL, K_EXCEEDS_N prepareExchange() validation failures
Keys INVALID_KEYS:PUBLIC_KEY_LENGTH, MISSING_DID ExchangeKeys validation failures
Cryptography SPLIT_FAILED, HMAC_FAILED, ECDH_FAILED XorIDA, HMAC, or X25519 operations fail
Reconstruction RECONSTRUCT_FAILED:HMAC_MISMATCH, INSUFFICIENT_SHARES completeExchange() failures
Format RECONSTRUCT_FAILED:INVALID_ENVELOPE, PRODUCT_MISMATCH Base45 decode or Xformat validation failures

Complete Error Reference

Code Sub-Code Meaning
INVALID_CONFIGN_TOO_SMALLn must be >= 2
INVALID_CONFIGK_TOO_SMALLk must be >= 2
INVALID_CONFIGK_EXCEEDS_Nk cannot exceed n
INVALID_CONFIGCOURIER_COUNT_TOO_LOWcourierCount must be >= n
INVALID_KEYSPUBLIC_KEY_LENGTHX25519 public key must be exactly 32 bytes
INVALID_KEYSSIGNING_KEY_LENGTHEd25519 signing key must be exactly 32 bytes
INVALID_KEYSMISSING_DIDDID string is required
SPLIT_FAILED(none)XorIDA split or envelope serialization failed
HMAC_FAILED(none)HMAC generation failed
RECONSTRUCT_FAILEDHMAC_MISMATCHShare integrity check failed -- data may be tampered
RECONSTRUCT_FAILEDINSUFFICIENT_SHARESFewer than k shares provided
RECONSTRUCT_FAILEDDESERIALIZEPayload deserialization failed
RECONSTRUCT_FAILEDINVALID_ENVELOPEBase45 or Xformat envelope is malformed
RECONSTRUCT_FAILEDPRODUCT_MISMATCHEnvelope product type is not XMEET
RECONSTRUCT_FAILEDUUID_MISMATCHShares belong to different exchanges
RECONSTRUCT_FAILEDUNPADPKCS#7 unpadding failed
ECDH_FAILEDKEY_DERIVATIONX25519 ECDH derivation failed
SIGNATURE_INVALID(none)Ed25519 signature verification failed
DUPLICATE_SHARE(none)Two or more shares have the same index
Error handling example
const result = await prepareExchange(aliceKeys, config);
if (!result.ok) {
  const { code, message, hint } = result.error;

  if (code.startsWith('INVALID_CONFIG')) {
    console.error('Configuration error:', message);
    console.log('Hint:', hint);
    // Fix n/k parameters and retry
  } else if (code.startsWith('INVALID_KEYS')) {
    console.error('Key validation failed:', message);
    // Regenerate keys with correct lengths
  } else {
    console.error('Unexpected error:', code, message);
  }

  throw new Error(message);
}
Appendix A2

Full API Surface

Complete function signatures and type definitions.

prepareExchange

prepareExchange(keys: ExchangeKeys, config: ExchangeConfig): Promise<Result<ExchangeResult, ExchangeError>>

Split an X25519 public key into K-of-N QR-printable shares for air-gapped delivery. Returns an ExchangeResult with an array of shares, each containing a Base45-encoded QR string.

completeExchange

completeExchange(qrShares: ReadonlyArray<string>, localPrivateKey?: CryptoKey): Promise<Result<ExchangeCompleteResult, ExchangeError>>

Reconstruct a counterparty's public key from scanned QR shares and optionally derive an ECDH shared secret. If no private key is provided, returns 32 zero bytes as sharedSecret placeholder.

Type Definitions

Core types
interface ExchangeKeys {
  publicKey: Uint8Array;        // X25519 public key, 32 bytes
  signingPublicKey: Uint8Array; // Ed25519 signing public key, 32 bytes
  did: string;                   // DID identifier
  label?: string;                // Optional human-readable label
}

interface ExchangeConfig {
  n: number;              // Total shares (2-255)
  k: number;              // Threshold to reconstruct (2-n)
  courierCount?: number;  // Number of independent couriers (default: n)
  label?: string;         // Optional label for this exchange
}

interface ExchangeResult {
  exchangeId: string;     // UUID identifying this exchange
  shares: ExchangeShare[]; // Array of n QR-printable shares
  ownerDid: string;       // DID of the key owner
  preparedAt: string;     // ISO 8601 timestamp
}

interface ExchangeShare {
  shareId: number;        // 1-based share index
  qrData: string;         // Base45-encoded Xformat envelope
  courier?: string;       // Optional courier identifier
}

interface ExchangeCompleteResult {
  exchangeId: string;              // UUID of the exchange
  peerPublicKey: Uint8Array;       // Reconstructed X25519 public key (32B)
  peerSigningPublicKey: Uint8Array; // Reconstructed Ed25519 signing key (32B)
  peerDid: string;                 // DID of the other party
  sharedSecret: Uint8Array;        // X25519 ECDH shared secret (32B), or zeroes if no private key
  completedAt: string;             // ISO 8601 timestamp
}

interface ExchangeError {
  code: string;           // Machine-readable error code with optional sub-code
  message: string;        // Human-readable description
  hint?: string;          // Actionable suggestion for fixing the error
}
Deployment

Package Deployment

Install and integrate airgapexchange into your application for air-gapped key exchange.

NPM Installation

Install from private registry
# Configure private npm registry
npm config set @private.me:registry https://private.me/npm/

# Install airgapexchange
npm install @private.me/airgapexchange

# Or with pnpm
pnpm add @private.me/airgapexchange

# Or with yarn
yarn add @private.me/airgapexchange
0.1.0
Current version
~45KB
Bundle size
2
Dependencies
65
Tests passing

TypeScript Integration

Basic usage
import {
  prepareExchange,
  completeExchange,
  type ExchangeKeys,
  type ExchangeConfig
} from '@private.me/airgapexchange';

// 1. Prepare exchange with your keys
const keys: ExchangeKeys = {
  publicKey: x25519PublicKey,        // 32-byte X25519 public key
  signingPublicKey: ed25519PublicKey, // 32-byte Ed25519 signing key
  did: 'did:key:z6Mk...',
  label: 'Alice Workstation'
};

const config: ExchangeConfig = {
  n: 3,    // Generate 3 shares
  k: 2,    // Require 2 to reconstruct
  label: 'Diplomatic Key Exchange'
};

const result = await prepareExchange(keys, config);
if (!result.ok) {
  console.error('Exchange preparation failed:', result.error);
  return;
}

// 2. Print QR codes and send via couriers
result.value.shares.forEach(share => {
  printQRCode(share.qrData, share.shareId);
});

// 3. On the receiving side, scan QR codes and complete exchange
const scannedQRs = [qr1Data, qr2Data]; // From QR scanner
const completed = await completeExchange(scannedQRs, bobPrivateKey);

if (completed.ok) {
  // Use the shared secret for secure communication
  const { sharedSecret, peerDid } = completed.value;
  initializeSecureChannel(sharedSecret, peerDid);
}

Environment Requirements

  • Node.js 18+ or modern browser with Web Crypto API support
  • TypeScript 5.0+ recommended (types included)
  • Dependencies: @private.me/crypto (XorIDA), @private.me/shareformat (Xformat)
  • QR code library: Use qrcode or similar for QR generation/scanning

Production Checklist

Before deploying to production
  • Test QR quality: Verify QR codes scan reliably at intended print DPI (300+ recommended)
  • Courier independence: Ensure K couriers cannot coordinate or communicate
  • Physical security: Use tamper-evident envelopes or diplomatic pouches
  • Fingerprint verification: Verify peer key fingerprints over out-of-band channel
  • Audit logging: Log all exchange preparation and completion events
  • Key lifecycle: Plan for key rotation and compromise recovery

Integration with Xlink

Once the air-gapped exchange completes, use the established trust to bootstrap Xlink agent-to-agent communication. The peer's public keys can be imported into Xlink's trust registry for ongoing encrypted messaging with replay protection and forward secrecy.

Bootstrap Xlink after exchange
import { Agent } from '@private.me/agent-sdk';
import { completeExchange } from '@private.me/airgapexchange';

// Complete air-gapped exchange
const completed = await completeExchange(qrShares, privateKey);
if (!completed.ok) throw new Error(completed.error);

// Register peer in Xlink trust registry
const agent = await Agent.create({ registry, transport });
await registry.register({
  did: completed.value.peerDid,
  publicKey: completed.value.peerPublicKey,
  signingPublicKey: completed.value.peerSigningPublicKey,
  scopes: ['xlink:send', 'xlink:receive'],
});

// Send encrypted Xlink envelope
await agent.send({
  to: completed.value.peerDid,
  payload: { message: 'Secure channel established' },
  scope: 'xlink:send',
});
Appendix A4

Operational Deployment

Production operational patterns for air-gapped key exchange systems.

Courier Selection

Independence: Choose couriers who do not coordinate or communicate. Use different organizations, different modes of transport, different routes. For 2-of-3, consider: diplomatic pouch (courier 1), commercial air freight (courier 2), trusted traveler (courier 3).

Trusted identity: Verify courier identity through out-of-band means before handing over QR codes. Use physical credentials, biometric verification, or pre-arranged recognition signals. A substituted courier carrying valid QR codes is indistinguishable from a legitimate courier without additional verification.

Redundancy: For high-value exchanges, use N > K to tolerate courier delays or failures. 3-of-5 means the exchange completes even if 2 couriers are delayed, compromised, or fail to arrive.

QR Code Handling

Print quality: Use high-DPI printers (300 DPI minimum) to ensure QR codes are scannable. Test scan reliability with multiple devices before handing to couriers. Include error correction level M (15% damage tolerance) or Q (25%) for physical resilience.

Physical security: Print QR codes on tamper-evident paper if available. Seal in opaque envelopes to prevent visual inspection during transit. For diplomatic contexts, use official pouch seals.

Destruction after use: Once the recipient scans K shares and completes the exchange, destroy all printed QR codes via shredding or incineration. Do not leave physical artifacts that could be recovered later.

Operational Security

Key fingerprint verification: After completing the exchange, verify the peer's Ed25519 signing public key fingerprint over a trusted out-of-band channel (e.g., voice call, in-person meeting). This prevents substitution attacks where all K couriers are compromised and deliver an attacker's QR codes.

Audit logging: Log all exchange preparation and completion events with timestamps, exchange IDs, and peer DIDs. Store logs separately from cryptographic keys. Use append-only tamper-evident logs for compliance contexts.

Key lifecycle: Treat air-gapped bootstrap keys as long-lived identity keys. Use ephemeral X25519 keypairs for each message session (via Xlink) to provide forward secrecy independent of the bootstrap keys. If bootstrap keys are compromised, execute a fresh air-gapped exchange to re-establish trust.