Loading...
private.me Docs
Explore ACIs
PRIVATE.ME · Technical White Paper

Replace any API connection in minutes

No API keys. No rotation. No downtime. Connect systems using cryptographic identity instead of bearer tokens. Run alongside your existing API, shift traffic gradually, remove keys when ready.

15s
Setup time
connect → send
Zero
Secret management
no API keys, ever
Safe
Migration
run alongside, switch when ready
🚀

Deploy in 15 Seconds

Production-ready Full Control setup for paying customers

Deploy to Vercel Deploy on Railway
✅ Pre-configured:
• Production Xpass
• K-of-N enforcement
• Air-gapped ready
• No trial limits
• VM-clone protection
• $150B deal-ready
Requires active subscription • Contact sales
v0.1.0 402 tests passing 0 npm deps Post-quantum ready Ed25519 + ML-KEM-768
THE ENTIRE FLOW

Pattern 1: Zero-Config Connect (DEFAULT)

As fast as 15 seconds
1
Connect to Service
const conn = await
  connect('payments')
Zero-config discovery
2
Send Securely
await conn.value.agent
  .send({ to, payload })
Signed • Encrypted

Zero-config service discovery in 2 steps

connect → send

Zero-config pattern for AI agents, microservices, and IoT. 90% of users start here.

Pattern 2: Manual Setup (Advanced)

For advanced users who need full control over registry and transport configuration:

1
Create Identity
const agent = await Agent.quickstart()
Generates Ed25519 identity
~1ms • Zero config
2
Send Message
await agent.send({
  to: targetDid,
  payload: { type: 'ping' },
  scope: 'read'
})
Encrypts & delivers
One-time setup
3
Receive & Verify
const msg = await agent.receive(envelope)
Verifies signature
Decrypts payload

Entity-to-entity authentication in 3 steps

Entity ↔ Entity. Not client → server.

Both have identity. Both verify. Both enforce scope.

Entity 1
did:key:z6Mk...
Ed25519 + ML-DSA-65
identity, not keys
Entity 2
did:key:z6Mn...
X25519 + ML-KEM-768
share 1
share 2
share 3
0 keys 0 secrets 0 tokens
Entity-to-entity authentication in 3 steps. Cryptographic identity eliminates API key rotation, sprawl, and revocation chaos.

Entity ↔ Entity. Not client → server.
Both have identity. Both verify. Both enforce scope.

ARCHITECTURAL PARADIGM SHIFT

Entity ↔ Entity Communication

Traditional API (One-Way)
CLIENT API Key: sk_abc123... REQUEST SERVER Identity: did:key:z6Mk... Asymmetric Trust Only server has identity
Xlink ACI (Two-Way)
ENTITY A Identity: did:key:z6MkA... SIGN + VERIFY SIGN + VERIFY ENTITY B Identity: did:key:z6MkB... Symmetric Trust Both sides have identity

Traditional APIs establish asymmetric connections: client (API key)server (identity). The client authenticates with a secret key, the server has an identity. This creates an inherent power imbalance and single point of compromise.

Xlink establishes symmetric connections: Entity A (identity)Entity B (identity). Both sides generate cryptographic identities (Ed25519 signing + X25519 key agreement). Both sides sign outgoing messages and verify incoming messages. Both sides enforce scopes and replay protection. Neither side "owns" the connection — it's a peer-to-peer trust relationship between two independent entities.

Property Traditional API (Client → Server) Xlink ACI (Entity ↔ Entity)
Client identity API key (bearer token, stateless) Cryptographic DID (Ed25519 keypair)
Server identity Domain + TLS cert (DNS-based) Cryptographic DID (same as client)
Authentication Client proves possession of key Both sides verify signatures (mutual)
Authorization Server-side policy (RBAC, scopes) Both sides enforce scopes (bilateral)
Message integrity HTTPS (transport-level only) Per-message signatures (end-to-end)
Replay protection None (stateless tokens) Nonce store on both sides
Compromise radius Leaked key = global access Compromised identity = single peer affected
Rotation Ongoing (keys expire, rotate) One-time setup (identity permanent)
Centralized gateway Required (API gateway, auth server) Optional (direct peer-to-peer works)
Dependency on DNS/PKI Yes (domain name + CA trust chain) No (cryptographic DIDs, no DNS)

The shift from client-server to entity-to-entity removes the asymmetry: both sides have identity, both sides verify, both sides enforce policy. No "client" vs "server" — just two independent entities with a bilateral trust relationship.

Section 01

Executive Summary

Xlink establishes entity-to-entity connections where both sides have cryptographic identities and verify each other's messages. No API keys, no asymmetric trust, no centralized gateways — just two independent entities with a bilateral trust relationship.

Two functions cover 80% of use cases: Agent.quickstart() generates an Ed25519 + X25519 identity with hybrid post-quantum key exchange (X25519 + ML-KEM-768, always-on) and registers it with default settings — zero configuration required. agent.send() encrypts a payload with AES-256-GCM, signs it with Ed25519 (+ ML-DSA-65 when postQuantumSig: true), and delivers it via any transport adapter. The receiver verifies signatures, checks replay protection, validates scope, and decrypts — automatically. Both sides perform the same verification steps, enforcing mutual trust rather than client-server hierarchy. For advanced use cases, Agent.create() accepts custom registry and transport configuration.

When you need information-theoretic security, split-channel mode shards messages via XorIDA (threshold sharing over GF(2)) and routes each share independently. An attacker who compromises any single channel learns nothing about the plaintext — not computationally hard to break, but mathematically impossible.

This entity-to-entity model removes the architectural assumptions of traditional APIs: no bearer tokens to rotate, no central auth servers to scale, no DNS dependency for trust. Each entity generates its own identity once, registers trusted peers, and communicates directly. The connection is symmetric, decentralized, and quantum-resistant by default.

Zero configuration out of the box. Zero npm runtime dependencies. Runs anywhere the Web Crypto API is available — Node.js, Deno, Bun, Cloudflare Workers, browsers. Dual ESM and CJS builds ship in a single package.

Quick Example

Multi-Agent Communication

Two agents establish cryptographic identities and communicate with full mutual authentication — no API keys, no central gateway, no configuration.

Alice and Bob: Peer-to-Peer Messaging

The following example demonstrates the complete lifecycle: both agents create identities using Agent.quickstart(), register with a shared trust registry, exchange messages with automatic encryption and signature verification, and validate each other's scope permissions.

Complete multi-agent example
import { Agent, MemoryTrustRegistry, LoopbackTransport } from '@private.me/agent-sdk';

// Shared infrastructure (in production, use HttpTrustRegistry + HttpsTransportAdapter)
const registry = new MemoryTrustRegistry();
const transport = new LoopbackTransport();

// Alice creates her identity
const alice = await Agent.quickstart({
  name: 'Alice',
  registry,
  transport,
});
console.log(`Alice DID: ${alice.did}`);
// Alice DID: did:key:z6MksZP8ChwZYSNgozYq...

// Bob creates his identity
const bob = await Agent.quickstart({
  name: 'Bob',
  registry,
  transport,
});
console.log(`Bob DID: ${bob.did}`);
// Bob DID: did:key:z6MkpH7eQ2KvYnPWbD...

// Alice sends a message to Bob
const sendResult = await alice.send({
  to: bob.did,
  payload: { type: 'greeting', text: 'Hello Bob!' },
  scope: 'chat',
});

if (!sendResult.ok) {
  throw new Error(`Send failed: ${sendResult.error}`);
}

// Bob receives and verifies the message
const envelope = transport.outbox[0];
const receiveResult = await bob.receive(envelope);

if (!receiveResult.ok) {
  throw new Error(`Receive failed: ${receiveResult.error}`);
}

console.log(`From: ${receiveResult.value.sender}`);
// From: did:key:z6MksZP8ChwZYSNgozYq...
console.log(`Payload:` , receiveResult.value.payload);
// Payload: { type: 'greeting', text: 'Hello Bob!' }
console.log(`Scope: ${receiveResult.value.scope}`);
// Scope: chat

// Bob replies to Alice
await bob.send({
  to: alice.did,
  payload: { type: 'response', text: 'Hi Alice!' },
  scope: 'chat',
});

// Alice receives Bob's reply
const reply = await alice.receive(transport.outbox[1]);
console.log(`Bob says: ${reply.value.payload.text}`);
// Bob says: Hi Alice!

Service-to-Service Communication

The same pattern applies to backend services. Here's a Payment service communicating with a Billing service:

Payment + Billing services
// Shared infrastructure
const registry = new HttpTrustRegistry({ baseUrl: 'https://trust.corp.example.com' });
const transport = new HttpsTransportAdapter({ baseUrl: 'https://relay.corp.example.com' });

// Payment service creates its identity
const paymentService = await Agent.quickstart({
  name: 'payment-service',
  registry,
  transport,
});

// Billing service creates its identity
const billingService = await Agent.quickstart({
  name: 'billing-service',
  registry,
  transport,
});

// Payment service notifies Billing about a completed transaction
await paymentService.send({
  to: billingService.did,
  payload: {
    transactionId: 'txn_abc123',
    amount: 99.50,
    currency: 'USD',
    customerId: 'cust_xyz789',
  },
  scope: 'payment:notify',
});

// Billing service receives and processes the notification
const txn = await billingService.receive(envelope);
console.log(`Transaction from: ${txn.value.sender}`);
console.log(`Amount: $${txn.value.payload.amount}`);

// Billing service sends confirmation back to Payment service
await billingService.send({
  to: paymentService.did,
  payload: {
    status: 'recorded',
    invoiceId: 'inv_2024_001',
  },
  scope: 'billing:confirm',
});
ZERO CONFIGURATION
Agent.quickstart() handles identity generation, key agreement setup (X25519 + ML-KEM-768), registry registration, and transport initialization. Both agents use the same API — no distinction between "client" and "server". The connection is symmetric, peer-to-peer, and quantum-resistant by default.

What Happens Under the Hood

When Alice calls send():

  1. Lookup: Queries the trust registry to retrieve Bob's public keys (Ed25519 signing, X25519 key agreement, ML-KEM-768 post-quantum KEM)
  2. Key Agreement: Performs hybrid ECDH (X25519 + ML-KEM-768 encapsulation) to derive a shared symmetric key
  3. Encryption: Encrypts the payload with AES-256-GCM using the derived key
  4. Signing: Signs the envelope with Alice's Ed25519 private key
  5. Transport: Sends the signed envelope to Bob via the transport adapter

When Bob calls receive():

  1. Signature Verification: Verifies Alice's Ed25519 signature using her public key from the registry
  2. Replay Protection: Checks the nonce store to ensure this envelope hasn't been seen before
  3. Scope Validation: Confirms Alice has permission to use the specified scope
  4. Key Agreement: Performs hybrid ECDH (X25519 + ML-KEM-768 decapsulation) to derive the same shared key
  5. Decryption: Decrypts the payload with AES-256-GCM
  6. Returns Result: Delivers the verified, decrypted payload to the application

Both sides perform the same verification steps — mutual authentication, mutual scope enforcement, mutual replay protection. Neither side has elevated privileges. The connection is truly peer-to-peer.

Section 02

Developer Experience

Xlink provides real-time progress tracking and 45+ structured error codes to help developers build reliable, debuggable M2M systems.

Progress Callbacks

Both send() and receive() operations support onProgress callbacks for tracking long-running operations, especially useful for split-channel mode where multiple shares are transmitted independently.

Progress tracking example
const envelope = await agent.send({
  to: recipientDid,
  payload: largeData,
  onProgress: async (event) => {
    switch (event.stage) {
      case 'encrypting':
        console.log('Encrypting payload...');
        break;
      case 'signing':
        console.log('Signing envelope...');
        break;
      case 'sending':
        console.log(`Sending share ${event.current}/${event.total}...`);
        break;
      case 'complete':
        console.log('Message sent successfully');
        break;
    }
  }
});

// Receive with progress tracking
const result = await agent.receive(envelope, {
  onProgress: async (event) => {
    if (event.stage === 'reconstructing') {
      console.log(`Reconstructing from ${event.current} shares...`);
    }
  }
});

Structured Error Handling

Xlink uses a Result<T, E> pattern with detailed error structures. Every error includes a machine-readable code, human-readable message, actionable hint, and documentation URL.

Error detail structure
interface ErrorDetail {
  code: string;         // e.g., 'INVALID_DID'
  message: string;      // Human-readable description
  hint?: string;        // Actionable suggestion
  field?: string;       // Field that caused the error
  docs?: string;        // Documentation URL
}

Error Categories

Xlink organizes 45+ error codes across 7 categories, making it easy to handle errors systematically:

Category Example Codes When
Identity INVALID_DID, KEYGEN_FAILED DID validation, key generation, signing
Envelope ENVELOPE_DECRYPTION_FAILED, PARSE_FAILED Envelope creation, encryption, decryption
Transport SEND_FAILED, NETWORK_ERROR Network failures, transport adapter errors
Registry DID_NOT_IN_REGISTRY, LOOKUP_FAILED Trust registry operations
Key Agreement ECDH_FAILED, INVALID_KEY_LENGTH X25519 ECDH derivation
Split-Channel HMAC_VERIFICATION_FAILED, INSUFFICIENT_SHARES XorIDA splitting, reconstruction, HMAC
Agent NONCE_REPLAY_DETECTED, SCOPE_DENIED High-level agent operations, replay prevention
COMPLETE ERROR REFERENCE
See Appendix A6: Error Taxonomy for the complete list of error codes, sub-codes, and detailed descriptions. The package README also includes comprehensive error handling examples.
Fast Onboarding

Fast Onboarding: < 2 Minute Setup

Zero-config service discovery and invite flow enable rapid M2M adoption. Setup time: < 2 minutes (vs 42-67 minutes for API keys).

The 2-Minute Setup Flow

Traditional API key setup requires 42-67 minutes of developer time per integration: account creation, API key generation, secret management setup, documentation reading, SDK installation, configuration, testing, and deployment. Xlink reduces this to under 2 minutes through zero-config service discovery and automatic trust establishment:

Complete setup: < 2 minutes
// Step 1: Initialize local identity (< 30 sec)
$ xlink init --name my-service
{
  "status": "initialized",
  "did": "did:key:z6MksZP8ChwZYSNgozYq...",
  "name": "my-service"
}

// Step 2: Connect to a service (< 90 sec)
$ xlink connect payments-service
{
  "status": "connected",
  "service": "payments-service",
  "did": "did:key:z6Mkf2rR8...",
  "endpoint": "https://api.payments.example.com",
  "elapsed_seconds": 1.3
}

// Step 3: Use it immediately
const { connect } = require('@private.me/agent-sdk');
const connection = await connect('payments-service');
await connection.value.agent.send({
  to: connection.value.did,
  payload: { action: 'createCharge', amount: 100 },
  scope: 'payments'
});

Zero-Config Discovery (3-Tier Lookup)

The connect() function accepts service names, domains, or URLs and automatically discovers connection details through a 3-tier lookup system:

Method Example Lookup
Public Registry connect('payments-service') Query xlink.registry.io for registered service
.well-known connect('api.example.com') Fetch https://api.example.com/.well-known/xlink.json
Direct URL connect('https://api.example.com/xlink') Use URL directly
AUTOMATIC TRUST ESTABLISHMENT
The first connection to a service automatically adds its public key to your local trust registry. Subsequent connections verify signatures against the stored key (TOFU: Trust On First Use). For enterprise deployments, admins can pre-populate the trust registry.

Invite Flow (< 10 sec creation, < 60 sec acceptance)

The invite system enables effortless service-to-service connections. Creating an invite takes < 10 seconds, accepting takes < 60 seconds, and the invite recipient can immediately use the connection.

Create invite (< 10 sec)
$ xlink invite billing-service --email billing@example.com
{
  "status": "created",
  "invite_url": "https://xlink.to/invite/a7Km9x...",
  "qr_code": "data:image/svg+xml,...",
  "expires_at": "2026-04-19T...",
  "message": "Share this link: https://xlink.to/invite/a7Km9x..."
}

When the recipient clicks the invite link, they see a one-click acceptance page with the inviter's service info. Accepting the invite automatically establishes the connection and adds both services to each other's trust registries.

Zero-Downtime Migration (Dual-Mode Adapter)

For existing M2M connections using API keys, Xlink provides a DualModeAdapter that runs Xlink and API key authentication simultaneously. This enables zero-downtime migration with gradual rollout and usage tracking:

Zero-downtime migration
const { DualModeAdapter } = require('@private.me/agent-sdk');

// Create dual-mode adapter (tries Xlink first, falls back to API key)
const adapter = new DualModeAdapter({
  xlink: xlinkAgent,        // Optional: add when ready
  fallback: {
    type: 'api-key',
    key: process.env.API_KEY,
    url: 'https://api.example.com',
  },
});

// Make calls (automatically tries Xlink → API key)
const result = await adapter.call('createCharge', { amount: 100 });

// Track migration progress
const metrics = adapter.getMetrics();
console.log(`Xlink usage: ${metrics.xlinkPercentage}%`);
// Output: "Xlink usage: 78%"

// Remove fallback when 100% migrated
adapter.removeFallback();

Comparison: Xlink vs Traditional API Keys

Aspect Traditional APIs Xlink
Setup Time 42-67 minutes (account + keys + config + docs + testing) < 2 minutes (init + connect + use)
Secret Management API keys in env vars, rotation every 90 days No keys, zero rotation
Discovery Manual documentation reading Zero-config 3-tier lookup
Invite Mechanism Email API key manually One-click invite link, < 10 sec creation
Acceptance Manual setup (42-67 min) One-click acceptance (< 60 sec)
Network Effect Linear (manual outreach) Each connection enables further invites
SUCCESS CRITERIA
Onboarding is considered successful when: (1) Setup < 2 min, (2) Invite creation < 10 sec, (3) Invite acceptance < 60 sec, (4) Connection immediately usable.
Developer Experience

Three Paths to Production

Xlink adoption follows three distinct business paths: greenfield connections (new builds), migration (existing APIs), and enterprise deployment (governance at scale). Each path has its own optimal onboarding flow.

STRATEGIC FRAMEWORK
Path 1: Greenfield — New M2M connections, no legacy infrastructure. Choose your speed tier (15s, 90s, or 10min).
Path 2: Migration — Existing API connections. Run ACI parallel, shift traffic gradually via Xfuse.
Path 3: Enterprise — Large-scale deployment. Configure trust policies, audit trails, and governance.

Path 1: Greenfield (New Connections)

For new M2M connections with no existing API infrastructure, Xlink offers three speed tiers. All three accomplish the same goal — establishing a secure identity-based connection — but at different setup speeds and automation levels.

Speed Tier 1: Zero-Click (15 seconds)

Fastest onboarding for developers trying Xlink for the first time. Share an invite code, paste it into your environment variables, and the SDK auto-discovers the recipient's identity and auto-registers your DID. First send succeeds immediately with zero manual configuration.

Zero-click setup via environment variable
// .env file
XLINK_INVITE_CODE=XLK-abc123def456

// Your code
const { Agent } = require('@private.me/agent-sdk');

// Agent auto-accepts invite and configures trust on first use
const agent = Agent.lazy({ name: 'my-service' });

// First send triggers identity generation + auto-registration
await agent.send({
  to: 'did:key:z6MkPartnerDID...',
  payload: { action: 'processData', data: { ... } },
  scope: 'integration'
});

Setup time: ~15 seconds
Best for: First-time developers, rapid prototyping, quick demos
Key benefit: Instant working demo → share invite with colleagues → immediate connection

Speed Tier 2: CLI-Guided (90 seconds)

Interactive CLI command that guides developers through framework-specific setup. Generates boilerplate code for Node.js, Python, Go, or Rust. Validates the connection with a test message before completing.

CLI-guided onboarding with framework templates
# Both commands are equivalent (xlink-onboard is an alias)
$ npx @private.me/agent-sdk init --invite XLK-abc123def456
# OR
$ npx @private.me/agent-sdk xlink-onboard --invite XLK-abc123def456

? Select your framework: Node.js (Express)
? Project name: my-integration

✓ Generated identity: did:key:z6MksZP8ChwZYSNgozYq...
✓ Configured trust registry
✓ Created src/xlink-client.ts
✓ Created .env with connection details
✓ Test message sent successfully

Ready to integrate. Run: npm start

Setup time: ~90 seconds
Best for: Developers integrating into existing systems, production setup
Key benefit: Production-ready code generated, validated connection before completion

Speed Tier 3: Deploy Button (10 minutes)

One-click infrastructure deployment for teams. Provisions complete production environment with Xlink pre-configured — Docker containers, Nginx reverse proxy, SSL certificates, health checks, and monitoring. Outputs production URLs when complete.

Deploy button provisions full infrastructure
<!-- Add to README.md -->
[![Deploy to Cloud](https://deploy.private.me/button)](https://github.com/private-me/xlink-infra/actions)

// Click button → GitHub Actions provisions:
// - DigitalOcean Droplet (or AWS EC2 / Google Cloud)
// - Docker Compose with Xlink services
// - Nginx reverse proxy + Let's Encrypt SSL
// - Prometheus monitoring + Grafana dashboards
// - Health check endpoints

// Outputs after 10 minutes:
{
  "service_url": "https://xlink.your-company.com",
  "did": "did:key:z6MksZP8ChwZYSNgozYq...",
  "status": "healthy"
}

Setup time: ~10 minutes
Best for: Teams deploying production infrastructure, platform integrations
Key benefit: Complete infrastructure → SSL, monitoring, backups — zero DevOps work

Speed Tier Comparison

Tier Setup Time Automation Level Output Best For
Zero-Click 15 seconds Full auto Working connection Demos, prototyping
CLI-Guided 90 seconds Interactive Production code Integration work
Deploy Button 10 minutes Infrastructure Live service + monitoring Team deployments

Path 2: Migration (Existing APIs)

Already have a working API connection? Migrate safely to Xlink using Xfuse — the threshold identity fusion bridge that runs ACI connections in parallel with your existing API, shifts traffic gradually, and deprecates the API when you're ready.

Parallel deployment: API + ACI via Xfuse
// Step 1: Deploy ACI alongside existing API (no downtime)
const { Xfuse } = require('@private.me/xfuse');

const bridge = await Xfuse.create({
  legacy: { apiKey: process.env.API_KEY, endpoint: 'https://api.legacy.com' },
  modern: { agent: xlinkAgent }
});

// Step 2: Mirror traffic (validate both paths)
const result = await bridge.send({
  mode: 'mirror',  // Send to both, compare results
  payload: { action: 'transfer', amount: 100 }
});

// Step 3: Shift traffic (10% → 50% → 100%)
bridge.setTrafficRatio({ aci: 0.5, api: 0.5 });

// Step 4: Deprecate API when ACI proves stable
bridge.setTrafficRatio({ aci: 1.0, api: 0.0 });

Migration time: Days to weeks (gradual traffic shift)
Downtime: Zero (parallel deployment)
Rollback: Instant (shift ratio back to API)
Learn more: Xfuse White Paper

Path 3: Enterprise (Governance at Scale)

Large organizations require centralized trust management, audit trails, and policy enforcement across hundreds of services. Enterprise deployment focuses on governance infrastructure rather than individual connection speed.

Enterprise: Trust registry + policy engine + audit
// Step 1: Configure centralized trust registry
const registry = await TrustRegistry.enterprise({
  mode: 'centralized',
  endpoint: 'https://trust.corp.example.com'
});

// Step 2: Define org-wide policies
const policy = await PolicyEngine.create({
  rules: [
    { scope: 'finance', require: ['2FA', 'audit-log'] },
    { scope: 'public', require: ['rate-limit'] }
  ]
});

// Step 3: Enable audit trail for compliance
const audit = await AuditLog.enterprise({
  retention: '7-years',  // SOX / SEC 17a-4
  encryption: 'org-key'
});

// All services inherit org config automatically
const agent = await Agent.create({
  name: 'payment-service',
  registry,  // Shared across org
  policy,    // Enforced centrally
  audit      // Compliance copy to SOC
});

Deployment scope: Organization-wide (10s–1000s of services)
Configuration: Once (all services inherit)
Compliance: SOC 2, ISO 27001, FedRAMP, HIPAA-ready
Learn more: AuthorizationAudit LogsCredentials

Choosing Your Path

Your adoption path depends on your starting point:

  1. Building something new?Greenfield Path — Start with Zero-Click (15s) for instant demo, upgrade to CLI (90s) for production code, or use Deploy Button (10min) for full infrastructure.
  2. Already have an API?Migration Path — Use Xfuse to run ACI parallel, shift traffic gradually (10% → 50% → 100%), deprecate API when stable. Zero downtime.
  3. Deploying across an organization?Enterprise Path — Configure trust registry, policies, and audit once. All services inherit org-wide governance automatically.
THREE DEPLOYMENT PATHS
Choose the path that fits your needs: Greenfield for new projects (fastest setup via invite codes), Migration for existing systems (zero-downtime transition), Enterprise for organizations (governance at scale). Each path optimizes for a different deployment scenario.
Section 03

The Problem

Machine-to-machine security today is a patchwork of API keys, OAuth client credentials, mTLS certificates, and API gateways — each with its own rotation schedule, configuration surface, and failure modes.

API keys leak. They end up in logs, git commits, environment variables shared over Slack, and CI pipelines with overly broad access. Rotation means touching every service that holds the key — a manual, error-prone process.

OAuth is complex. Client credentials flow requires token endpoints, scopes, refresh logic, and revocation. Every new service needs a registration, a secret, and a grant configuration.

mTLS certs expire. Certificate lifecycle management is a full-time job. Renewal failures cause outages. CA compromise is a single point of failure for the entire mesh.

Gateways add latency and cost. Centralized API gateways become bottlenecks, introduce single points of failure, and charge per-request fees that scale with traffic.

Property API Keys OAuth 2.0 mTLS Xlink
Initial setup Minutes Hours Days 5 lines
Key rotation Manual Token refresh Cert renewal Never*
E2E encryption No No Transport only Yes
Forward secrecy No No Optional Auto ECDH
Non-repudiation No No No Ed25519 + ML-DSA-65
Replay prevention No Partial Partial Nonce store
Info-theoretic mode No No No XorIDA split
npm dependencies Varies 10-50+ OS-level 0

* Ed25519 identity keys are permanent. No expiry, no renewal. New identity = new DID. See Limitations for details.

Two-Way Communication (vs One-Way APIs)

Traditional APIs provide one-way request/response flows. When bidirectional communication is needed, systems cobble together separate mechanisms: the client makes HTTP requests in one direction, and the server sends webhooks or uses SSE/WebSockets in the reverse direction. This creates architectural complexity, separate authentication flows, and webhook delivery failures.

Xlink provides native two-way peer-to-peer communication. Both services are agents with DIDs, public keys, and the ability to send and receive messages. There is no client/server distinction at the protocol level — both parties are peers. This eliminates the need for webhooks, polling, and separate bidirectional channels.

Aspect Traditional APIs Xlink
Communication Model Client → Server (one-way) Peer ↔ Peer (native two-way)
Reverse Direction Webhooks (separate HTTP POST, delivery failures, retries, auth) Same protocol (send message to peer's DID)
Authentication Two separate flows (API key for requests, webhook secret for callbacks) Single mechanism (Ed25519 signatures both directions)
Delivery Guarantees Best-effort webhooks, no built-in retry, manual dead-letter queues Store-and-forward relay with 7-day TTL
Firewall Traversal Webhook receiver must be publicly accessible (or use ngrok tunnels) Both peers can be behind NAT/firewall (pull messages from relay)
Complexity 2 separate subsystems (API client + webhook server) 1 agent (send + receive)
ARCHITECTURAL SIMPLIFICATION
Xlink's peer-to-peer model eliminates webhook endpoints, delivery failures, authentication duplication, and NAT traversal issues. Both services use the same agent.send() and agent.receive() primitives regardless of direction.
Two-way communication example
// Service A sends request to Service B
const request = await serviceA.send({
  to: serviceBDid,
  payload: { action: 'processPayment', amount: 100 },
  scope: 'payments',
});

// Service B receives request
const inbound = await serviceB.receive(request);

// Service B sends response back to Service A
const response = await serviceB.send({
  to: serviceADid,
  payload: { status: 'success', transactionId: 'tx_123' },
  scope: 'payments',
});

// Service A receives response (no webhook needed)
const result = await serviceA.receive(response);

With APIs, the reverse direction would require Service A to expose a webhook endpoint, implement authentication, handle delivery failures, and manage retries. With Xlink, both directions use the same authenticated message-passing primitives. The relay server handles store-and-forward, delivery guarantees, and NAT traversal automatically.

The Old Way

Service A API key API GATEWAY single point of failure per-request cost Service B KEY ROTATION Manual. Touch every service. CERT EXPIRY mTLS renewal = outages NO E2E ENCRYPT Plaintext at every hop Leaked key = full impersonation

The New Way

Service A did:key:z6Mk... encrypt + sign XLINK ENVELOPE AES-256-GCM + Ed25519 nonce + scope + timestamp verify + decrypt Service B did:key:z6Mk... NO KEY ROTATION Ed25519 keys are permanent FORWARD SECRECY Per-message ECDH keys NON-REPUDIATION Ed25519 sig on every msg Optional: XorIDA split-channel — information-theoretic security, zero computational assumptions
Section 04

Real-World Use Cases

Six scenarios where Xlink replaces traditional API key management with Authenticated Cryptographic Interfaces.

📡
IoT
Fleet Telemetry

Each sensor gets a deterministic identity from a factory-burned seed. Signed telemetry envelopes flow to gateways. No API keys to rotate across 10,000 devices.

Agent.fromSeed() + createSignedEnvelope()
🤖
AI / ML
Agent Orchestration

AI agents negotiate tasks via encrypted envelopes with scope-based authorization. The orchestrator verifies each agent’s identity before dispatching work.

Agent.create() + agent.send() + scopes
🌐
Platform
Microservice Mesh

Services authenticate via DID instead of shared secrets. ECDH forward secrecy protects inter-service traffic. No certificate authority to manage.

HttpsTransportAdapter + MemoryTrustRegistry
🏥
Healthcare
Clinical Data Exchange

PHI travels via split-channel — any single intercepted channel reveals zero patient data. HIPAA compliance by mathematical guarantee, not policy alone.

security: 'high' (auto 2-of-3)
💹
Financial
Trade Messaging

Order routing with non-repudiation. Ed25519 signatures provide cryptographic proof of trade instructions. Timestamp validation prevents replay attacks.

Ed25519 non-repudiation + 30s window
🏛
Government
Secure Comms

Split-channel with 3-of-5 threshold across classified and unclassified networks. Information-theoretic security exceeds AES-256 — quantum-proof by construction.

security: 'critical' (3-of-5)
Section 05

Solution Architecture

Six composable modules. Each can be used independently or combined through the high-level Agent ACI.

Envelope
v1 / v2 / v3 + Xchange
AES-256-GCM encrypt-then-sign
Ed25519 + ML-DSA-65 dual signatures (v3)
Optional signed-only (cleartext) mode
Trust Registry
3 implementations
Memory (dev), HTTP (prod), did:web
Scope-based authorization
Revocation support
Nonce Store
Replay prevention
Memory (single-node) or Redis (distributed)
SET NX + TTL for atomic dedup
Configurable TTL and cleanup
Key Agreement
X25519 + ML-KEM-768
Hybrid PQ KEM (always-on, v2+)
Ephemeral key pair per message
HKDF-SHA256 over both shared secrets
Transport
Pluggable
HTTPS POST (server-to-server)
Gateway (Xail inbox delivery)
Custom adapter interface
Alice did:key:z6Mk... Ed25519 + X25519 Encrypt AES-256-GCM Sign Ed25519 Transport HTTPS POST Verify Sig + Nonce Bob did:key:z6Mk... Decrypt STANDARD SEND/RECEIVE FLOW Encrypt → Sign → Transport → Verify → Decrypt
Section 05a

The One-Time Setup

Five lines of code. No configuration files, no gateway dashboards, no certificate authorities.

One-time setup
import { Agent, MemoryTrustRegistry, HttpsTransportAdapter } from '@private.me/agent-sdk';

const registry  = new MemoryTrustRegistry();
const transport = new HttpsTransportAdapter({ baseUrl: 'https://api.example.com' });
const agent    = (await Agent.create({ name: 'MyService', registry, transport })).value!;

// That's it. agent.did is your identity. agent.send() encrypts + signs + delivers.
await agent.send({ to: recipientDid, payload: { action: 'hello' }, scope: 'chat' });
Compare with API gateway setup
Create gateway instance → configure routes → set up auth provider → generate API keys → configure rate limits → set up key rotation schedule → deploy gateway → update every client → monitor key expiry → handle rotation failures... Xlink: 5 lines, done.
Section 05b

Forward Secrecy

Hybrid post-quantum key agreement: X25519 ECDH + ML-KEM-768 KEM (always-on for v2+ agents). Ephemeral key pairs provide forward secrecy.

The sender generates a fresh ephemeral X25519 key pair per message. The shared secret is derived from the sender's ephemeral private key and the recipient's static X25519 public key. The ephemeral public key is included in the envelope so the receiver can derive the same shared secret.

Compromise of long-term keys does not reveal past messages. Each message uses a unique shared secret derived from a unique ephemeral key pair. Past messages are protected even if both parties' long-term keys are later compromised.

Alice (Sender) 1. Generate ephemeral X25519 2. ECDH: eph.priv x bob.pub 3. Derive AES-256-GCM key 4. Include eph.pub in envelope envelope + ephemeralPub Bob (Receiver) 1. Import eph.pub from envelope 2. ECDH: bob.priv x eph.pub 3. Same AES-256-GCM key 4. Decrypt payload Fresh ephemeral key per message — past messages safe even if long-term key leaks
SHA-256 fallback
When the recipient has not published an X25519 key, the shared key is derived as SHA-256(sort(pubA, pubB)). This is deterministic and not forward-secret. It exists for backward compatibility and is automatically upgraded when X25519 keys become available.
Section 05c

Split-Channel Mode

Information-theoretic security via XorIDA threshold secret sharing. Automatic risk-based activation.

The SDK automatically applies split-channel protection (2-of-3 XorIDA threshold sharing) for high-risk operations: high-value transfers, cross-organization communication, and sensitive scopes. The plaintext is split into N shares (default 3) over GF(2), with a reconstruction threshold of K (default 2). Each share is independently encrypted, signed, and transmitted via separate channels.

Automatic security — no manual flags
// SDK auto-applies split-channel for high-risk operations
await agent.send({
  to: recipientDid,
  payload: { amount: 500000, action: 'transfer' },  // High value → auto 2-of-3
  scope: 'custody',                              // Sensitive scope → auto 2-of-3
  action: 'execute'                             // Critical action → auto 2-of-3
});

// Manual override if needed (most users won't use this)
await agent.send({
  to: recipientDid,
  payload: { data: 'classified' },
  scope: 'secure',
  security: 'high',  // Force 2-of-3 even if policy wouldn't auto-apply
});
Backward Compatibility
Legacy splitChannel: true and splitChannelConfig flags are still supported but deprecated. New code should use security: 'auto' | 'standard' | 'high' | 'critical' for clearer intent.
Plaintext + HMAC key XorIDA GF(2) split <1ms typical Share 1 Share 2 Share 3 Channel A Channel B X Intercepted Reconstruct HMAC verify K=2 of N=3 Plaintext Restored Any 2 of 3 shares reconstruct. A single intercepted share reveals zero information.
Information-theoretic guarantee
XorIDA operates over GF(2) (binary field). Any K-1 or fewer shares reveal exactly zero information about the plaintext. This is not computational security (breakable with enough computing power) — it is information-theoretic security (mathematically impossible to break regardless of computing power, including quantum computers).

Multi-Transport Routing

For true channel separation, provide one transport adapter per share. The SDK routes share[i] to transports[i % transports.length] using modulo arithmetic. With 3 shares and 3 transports, share 0 goes to transport 0, share 1 to transport 1, share 2 to transport 2. With 3 shares and 2 transports, shares route as 0→0, 1→1, 2→0 (wraps around).

Multi-transport — 3 independent channels
const agent = await Agent.create({
  name: 'SecureAgent',
  registry,
  transport: [
    new HttpsTransportAdapter({ baseUrl: 'https://ch1.example.com' }),
    new HttpsTransportAdapter({ baseUrl: 'https://ch2.example.com' }),
    new HttpsTransportAdapter({ baseUrl: 'https://ch3.example.com' }),
  ],
});

// Shares route: [0 → ch1, 1 → ch2, 2 → ch3]
await agent.send({
  to: recipientDid,
  payload: sensitiveData,
  scope: 'classified',
  security: 'high',  // Auto 2-of-3 split across 3 transports
});

Channel independence is the developer’s responsibility. For maximum security, use different infrastructure providers, different network paths, or different geographic regions for each transport. The SDK warns at runtime if transports.length < totalShares.

Scenario Shares Transports Routing Security
Ideal 3 3 0→0, 1→1, 2→2 Full channel separation
Partial 3 2 0→0, 1→1, 2→0 Two channels (share reuse)
Single 3 1 0→0, 1→0, 2→0 No channel separation
Transport array vs single adapter
The transport parameter accepts either a single XailTransportAdapter or an array of adapters. Passing a single adapter is equivalent to transport: [adapter] — all shares route through it. Use arrays to achieve true multi-path delivery. See distributed messaging patterns for routing architecture guidance.

V3 Protocol (Default for Split-Channel)

When split-channel mode is activated (via automatic policy or explicit security: 'high') without Xchange, the SDK uses V3 envelopes with full post-quantum protection and three independent cryptographic layers:

Layer Technology Purpose Standard
1. Payload XorIDA (GF(2)) Information-theoretic splitting Proprietary (patent-protected)
2. Key Exchange X25519 + ML-KEM-768 Hybrid PQ session keys FIPS 203 (always-on)
3. Authentication Ed25519 + ML-DSA-65 Dual signature verification FIPS 204 (opt-in via postQuantumSig: true)

V3 is the default for split-channel mode. Each share is independently encrypted with AES-256-GCM using a session key derived from hybrid KEM. The sender generates an ephemeral X25519 key pair AND performs ML-KEM-768 encapsulation per message. Both shared secrets combine via HKDF-SHA256. Authentication uses Ed25519 (always) plus ML-DSA-65 (when enabled). V3 provides defense-in-depth: compromise of any single cryptographic primitive does not break the system.

Envelope version auto-negotiation
Agents negotiate envelope versions automatically based on registered key capabilities. A v3-capable agent falls back to v2 when the recipient has no ML-DSA key, or v1 when the recipient has no ML-KEM key. No manual configuration required. Versioning is transparent to application logic.

Xchange Mode (Opt-In Performance)

For latency-critical workloads (IoT, high-frequency M2M, real-time agents), Xchange mode trades per-share encryption and KEM for ~180x faster operation. Activated explicitly via xchange: true on both agent creation and send.

Xchange mode — opt-in
// Agent opts in to Xchange support
const agent = await Agent.create({
  name: 'FastAgent',
  registry, transport,
  xchange: true,
});

// Xchange on send (security policy still applies)
await agent.send({
  to: recipientDid,
  payload: sensorReading,
  scope: 'telemetry',
  security: 'high',  // Split-channel with Xchange speed
});
Xchange keeps
retained
XorIDA split (information-theoretic)
HMAC integrity per share
Ed25519 sender authentication
Xchange trades
traded for speed
No per-share AES-256-GCM encryption
No KEM key agreement
Single security layer + auth

Default split-channel uses V3 with three independent cryptographic layers: XorIDA payload split, hybrid PQ KEM (X25519 + ML-KEM-768), and dual signatures (Ed25519 + ML-DSA-65). Xchange mode is for scenarios where latency matters more than defense-in-depth.

Section 05d

Identity Layers in PRIVATE.ME

Three composable identity layers. Xlink is Layer 1. XID adds ephemeral unlinkability. Xfuse adds threshold convergence for high-assurance scenarios.

The PRIVATE.ME platform provides a three-layer identity architecture. Each layer builds on the previous, offering progressively stronger privacy guarantees and multi-factor assurance. Applications choose the layer that matches their security requirements.

Layer 1: Xlink — Cryptographic Identity

Xlink provides the foundational identity layer. Every agent has a persistent DID (did:key:z6Mk...) backed by Ed25519 signing and X25519 key agreement. This is the identity used for most M2M communication, agent-to-agent messaging, and service authentication.

Layer 1 — Persistent identity
import { Agent } from '@private.me/agent-sdk';

// Create a persistent agent identity
const agent = (await Agent.create({
  name: 'MyService',
  registry,
  transport
})).value!;

// DID is stable across sessions
console.log(agent.did);
// did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH

// Same DID on every restart (if keys persisted)
const pkcs8 = await agent.exportPKCS8();
const restored = (await Agent.importIdentity({ pkcs8, registry, transport })).value!;
console.log(restored.did === agent.did);  // true
When to use Layer 1
Most applications stay at Layer 1. Persistent identity simplifies trust relationships, enables long-term authentication, and provides accountability. Use Layer 1 for internal services, trusted partner communication, and scenarios where identity continuity matters more than unlinkability.

Layer 2: XID — Ephemeral Unlinkable Identity

XID adds per-verifier ephemeral DIDs derived from a master seed via HKDF. Each relationship sees a different DID. Cross-context tracking becomes impossible. DIDs rotate on a configurable schedule (epoch-based). The master seed is XorIDA-split and never stored in plaintext.

Layer 2 — Ephemeral per-verifier DIDs
import { EphemeralIdentity } from '@private.me/xid';

// Create ephemeral identity manager (master seed is split-protected)
const eph = await EphemeralIdentity.create({
  epochDurationMs: 86400000,  // 24 hours
  splitConfig: { totalShares: 3, threshold: 2 }
});

// Derive ephemeral DID for specific verifier
const did1 = await eph.deriveForVerifier('ServiceA');
const did2 = await eph.deriveForVerifier('ServiceB');

console.log(did1 !== did2);  // true — unlinkable across contexts

// DID rotates on epoch boundary
const nextEpoch = await eph.deriveForVerifier('ServiceA', epoch + 1);
console.log(nextEpoch !== did1);  // true — unlinkable across time
Regulatory compliance
XID ephemeral identity satisfies eIDAS 2.0 ARF unlinkability requirements, GDPR data minimization, and ISO 24745 cancelable biometrics. The master seed never exists in plaintext at rest. Each epoch rotation invalidates all previous DIDs. Forward secrecy is built-in.

See the XID white paper for full technical details on HKDF derivation schedules, epoch management, and split-protected seed storage.

Layer 3: Xfuse — Threshold Identity Convergence

Xfuse adds K-of-N threshold convergence for high-assurance scenarios. Identity is established by presenting K independent signals (password + biometric + device credential + trusted third party attestation). Signals converge via XorIDA to derive a session-bound DID. No single signal is sufficient.

Layer 3 — Threshold convergence (2-of-3)
import { FusionManager } from '@private.me/xfuse';

// Configure threshold identity fusion
const fusion = new FusionManager({
  threshold: 2,
  totalSignals: 3,
  ial: 'IAL2',  // NIST 800-63A assurance level
});

// Enroll three independent signals
await fusion.enrollSignal({
  type: 'password',
  value: hashedPassword
});
await fusion.enrollSignal({
  type: 'biometric',
  value: fingerprintTemplate
});
await fusion.enrollSignal({
  type: 'device',
  value: tpmAttestation
});

// Authenticate with any 2 of 3 signals
const result = await fusion.converge([
  { type: 'password', value: hashedPassword },
  { type: 'biometric', value: currentFingerprint },
]);

// Session-bound DID derived from converged signals
console.log(result.did);  // did:key:z6Mk... (ephemeral, session-scoped)
console.log(result.assuranceLevel);  // IAL2
Defense & Financial use cases
Xfuse satisfies CMMC Level 3 multi-factor requirements, financial industry KYC/AML identity proofing, and classified data access controls. Biometric templates are cancelable per ISO 24745 (derived via HKDF, not raw biometrics). Device credentials use TPM-backed attestation. Third-party signals support federated trust.

See the Xfuse white paper for threshold convergence algorithms, signal diversity requirements, assurance level mapping (IAL1/IAL2/IAL3), and MPC-verified convergence.

Composability & Layer Selection

Applications select the identity layer at integration time. The three layers are independent but composable. A single codebase can use Layer 1 for internal services, Layer 2 for customer-facing endpoints, and Layer 3 for administrative access.

Layer Identity Model Privacy Guarantee Use Cases
Layer 1: Xlink Persistent DID Encrypted + signed messaging M2M, agent communication, internal APIs
Layer 2: XID Ephemeral per-verifier DID Cross-context unlinkability Customer apps, eIDAS compliance, GDPR
Layer 3: Xfuse K-of-N threshold convergence Multi-factor high-assurance Defense, finance, healthcare, gov
Layer selection at integration time
// Internal service — Layer 1 (persistent identity)
const internalAgent = await Agent.create({ name: 'InternalService', registry, transport });

// Customer app — Layer 2 (ephemeral unlinkable)
const customerEph = await EphemeralIdentity.create({ epochDurationMs: 3600000 });
const customerDid = await customerEph.deriveForVerifier('CustomerPortal');

// Admin access — Layer 3 (threshold convergence)
const adminFusion = new FusionManager({ threshold: 3, totalSignals: 4, ial: 'IAL3' });
const adminResult = await adminFusion.converge(signals);

Layer 1 (Xlink) is the foundation. Layer 2 (XID) and Layer 3 (Xfuse) are optional enhancements. Most applications use Layer 1 exclusively. Add Layer 2 when unlinkability is required. Add Layer 3 when regulatory compliance demands multi-factor high-assurance identity.

Section 06

Identity & Persistence

Ed25519 signing + X25519 key agreement via Web Crypto API. Hybrid post-quantum: ML-KEM-768 for key exchange (always-on), ML-DSA-65 for signatures (opt-in). Multiple persistence strategies for different environments.

DID:key Format

Each agent identity is encoded as a did:key DID string: did:key:z6Mk.... The DID embeds the raw Ed25519 public key with a multicodec prefix (0xed01) and base58btc encoding. Anyone with the DID can verify signatures without a network lookup.

Persistence Strategies

Deterministic Seed
IoT
Agent.fromSeed(32-byte seed)
HKDF-SHA256 derives Ed25519 + X25519
Same seed = same DID on every boot
Raw Key Export
Advanced
agent.exportSeeds() for raw 32-byte keys
Cross-device key transfer
Not the original HKDF seed (one-way)
Composable
Factory
Agent.fromParts() — sync, no async
Pre-built identity + registry + transport
Different lifecycle stages
Section 07

Complete Flow

End-to-end: identity creation through message delivery and verification.

Sender Pipeline

1. Agent.create() generates Ed25519 + X25519 keys (+ ML-DSA-65 keys when postQuantumSig: true) and registers with the trust registry.
2. agent.send() resolves the recipient DID from the registry.
3. Hybrid key exchange: X25519 ECDH + ML-KEM-768 KEM, combined via HKDF-SHA256 (always-on for v2+ agents). SHA-256 fallback for v1 peers.
4. Payload encrypted with AES-256-GCM (12-byte IV, fresh per message).
5. Ciphertext signed with Ed25519 (+ ML-DSA-65 dual signature in v3 envelopes).
6. Envelope assembled: v2/v3, sender DID, recipient DID, timestamp, nonce, scope, payload, signature(s), ephemeralPub, kemCiphertext.
7. Transport adapter delivers the envelope.

Receiver Pipeline

8. agent.receive() validates envelope version (v1/v2/v3) and algorithm.
9. Timestamp checked against configurable window (default 30s).
10. Nonce checked against NonceStore — rejects duplicates (replay prevention).
11. Sender DID resolved from trust registry — must be registered and not revoked.
12. Ed25519 signature verified. ML-DSA-65 signature also verified if present (v3 envelopes).
13. Sender's scope validated against the claimed scope in the envelope.
14. Shared key derived via hybrid KEM (X25519 + ML-KEM-768) or SHA-256 fallback for v1.
15. Payload decrypted with AES-256-GCM.
16. JSON parsed and returned as AgentMessage.

Section 08

Integration Patterns

Four patterns for different deployment contexts.

Express Middleware

Express middleware
import express from 'express';
const app = express();

// Verify incoming agent envelopes
app.post('/api/messages', agent.middleware(), (req, res) => {
  const msg = req.agentMessage;
  console.log('From:', msg.sender, 'Scope:', msg.scope);
  res.json({ ok: true });
});

Registry Auth Middleware

Registry auth
import { createRegistryAuthMiddleware } from '@private.me/agent-sdk';

// GET /registry/resolve/:did  → public (no auth)
// POST /registry/register     → requires Bearer token
app.use('/registry', createRegistryAuthMiddleware(process.env.REGISTRY_ADMIN_TOKEN));

IoT Composable Pattern

Minimal IoT — no Agent class needed
import { generateIdentity, createSignedEnvelope, splitForChannel } from '@private.me/agent-sdk';

const id = (await generateIdentity()).value!;
const reading = new TextEncoder().encode(JSON.stringify({ temp: 22.5 }));

// Signed-only envelope (no encryption, integrity-only)
const envelope = await createSignedEnvelope({
  senderDid: id.did, recipientDid: gatewayDid,
  scope: 'telemetry', plaintext: reading, privateKey: id.privateKey,
});

// Or split across channels for redundancy
const shares = await splitForChannel(reading, { totalShares: 3, threshold: 2 });

Signed-Only Telemetry

Gateway accepting cleartext + encrypted
// Gateway accepts both encrypted and signed-only envelopes
const msg = await gateway.receive(envelope, { allowCleartext: true });
if (msg.ok) {
  console.log(msg.value.payload); // works for both modes
}
Section 09

Security Properties

Seven layers of defense. Each independently verifiable.

Property Mechanism Guarantee
Confidentiality AES-256-GCM per message Payload encrypted in transit and at rest
Authentication Ed25519 + ML-DSA-65 (dual) Sender identity verified on every envelope (PQ-safe with opt-in)
Integrity Encrypt-then-sign Any modification fails verification
Non-repudiation Ed25519 + ML-DSA-65 dual signature Sender cannot deny sending (quantum-safe with opt-in)
Forward secrecy X25519 + ML-KEM-768 hybrid Post-quantum forward secrecy (always-on)
Replay prevention Nonce store + timestamp Duplicate envelopes rejected
Info-theoretic XorIDA split-channel K-1 shares reveal zero bits

Supply Chain Security

Zero npm runtime dependencies. The SDK depends only on workspace packages (@private.me/shared, @private.me/crypto) which are part of the same monorepo. No third-party code executes at runtime. This eliminates the entire class of supply chain attacks from compromised npm packages.

Comparison to Alternatives

Property Xlink TLS Signal Protocol
E2E encryption Yes Transport only Yes
Forward secrecy Yes (Hybrid PQ KEM) Yes (DHE) Yes (Ratchet)
Info-theoretic Yes (XorIDA) No No
Non-repudiation Ed25519 + ML-DSA-65 No No
Replay prevention Nonce store Seq numbers Chain keys
Zero npm deps Yes N/A No
M2M focused Yes General Person-to-person
Section 10

Benchmarks

Performance characteristics measured on Node.js 22, Apple M2.

<1ms
Identity generation
<1ms
Encrypt + sign (per share)
<1ms
Verify + decrypt (per share)
~33ms
XorIDA 1MB split
402
Tests passing
Operation Time Notes
Ed25519 keygen <1ms Web Crypto API native
X25519 keygen <1ms Web Crypto API native
ECDH key agreement <1ms Ephemeral + derive
AES-256-GCM encrypt (1KB) <0.5ms Hardware-accelerated
Ed25519 sign <0.5ms Covers ciphertext bytes
ML-KEM-768 encapsulate ~2.7ms FIPS 203 — @noble/post-quantum
ML-KEM-768 decapsulate ~2.9ms FIPS 203 — @noble/post-quantum
ML-DSA-65 sign ~10ms FIPS 204 — opt-in dual signature
ML-DSA-65 verify ~10ms FIPS 204 — opt-in dual signature
Full send pipeline (Xchange) ~1ms Random key + encrypt + XorIDA split + sign
Full receive pipeline (Xchange) ~1ms Verify + reconstruct + HMAC + decrypt
Full send pipeline (split-channel) ~5ms KEM + per-share AES-GCM + XorIDA split + Ed25519
Full receive pipeline (split-channel) ~4ms Verify + KEM decaps + per-share decrypt + reconstruct
Full roundtrip (split-channel + ML-DSA-65) ~29ms Three layers + dual PQ signatures
XorIDA split (1MB, 3 shares) ~52ms GF(2) — 15x faster than Shamir's at 1MB
XorIDA reconstruct (1MB) ~33ms Including HMAC verification
Nonce check (memory) <0.1ms Map lookup + TTL check

XorIDA vs AES-256-GCM — API Payload Performance

Fairness Disclosure
AES-256-GCM benchmarks use the Web Crypto API with hardware AES-NI acceleration. XorIDA runs in pure JavaScript (GF(2) XOR operations). Both measured on the same machine under the same conditions. XorIDA is faster at typical API payload sizes despite running without hardware acceleration.

Typical ACI traffic — auth tokens, JSON responses, webhooks, chat messages — is under 1 KB. At these sizes, XorIDA’s simple XOR-only arithmetic completes a full split-and-reconstruct roundtrip 2–11× faster than AES-256-GCM can encrypt-and-decrypt, while providing strictly stronger (information-theoretic) security.

256-Byte Roundtrip — Visual Comparison

XorIDA 2-of-2
35µs
XorIDA 2-of-3
41µs
AES-256-GCM
122µs

Shorter bar = faster. 256B payload, 2,000 iterations, median roundtrip time.

Full Comparison — API Payload Sizes

Payload XorIDA 2-of-2 XorIDA 2-of-3 AES-256-GCM Ratio (2-of-2) Real-World Example
64B 14µs 17µs 160µs 11.4× faster IoT sensor reading, heartbeat
128B 23µs 26µs 160µs 7.0× faster Auth token, session ticket
256B 35µs 41µs 122µs 3.5× faster Chat message, SMS-length payload
512B 34µs 39µs 138µs 4.0× faster Webhook, API key exchange
1KB 58µs 107µs 140µs 2.4× faster REST API JSON response
2KB 211µs 262µs 142µs 1.5× slower GraphQL response
4KB 340µs 313µs 134µs 2.5× slower Large API response
8KB 644µs 883µs 227µs 2.8× slower Crossover zone

Node.js 22 • 2,000 iterations per size • Median roundtrip (split+reconstruct / encrypt+decrypt)

Honest About Tradeoffs
Above ~1–2 KB, hardware-accelerated AES-256-GCM is faster. But typical API payloads — auth tokens, JSON responses, chat messages, webhook notifications — are under 1 KB, exactly where XorIDA excels. For the payloads Xlink actually processes, XorIDA is 2–11× faster than AES-256 while providing strictly stronger security.

Xlink vs Competitors — API Crypto Overhead

Full send + receive roundtrip measured end-to-end with real crypto operations. Competitor estimates use our measured primitive timings (same algorithms, same JS runtime) as reference. All columns use Xchange mode (opt-in performance path) for an apples-to-apples comparison; Xlink’s default split-channel adds KEM + per-share encryption (~9ms roundtrip) for three independent security layers.

256-Byte API Payload — Full Roundtrip

Xlink (Xchange)
1.9ms
Tuta
4.2ms
Signal
5.0ms
Apple PQ3
5.0ms

Shorter bar = faster. 256B payload, 200 iterations, median roundtrip time.

Full Comparison — All API Payload Sizes

Payload Xlink (Xchange) Tuta Signal Apple PQ3 Use Case
64B 2.0ms 4.2ms 5.0ms 5.0ms Auth token, session ID
128B 1.8ms 4.2ms 5.0ms 5.0ms Webhook notification
256B 1.9ms 4.2ms 5.0ms 5.0ms Small JSON response
512B 1.8ms 4.2ms 5.0ms 5.0ms API request body
1KB 1.9ms 4.2ms 5.0ms 5.0ms Large JSON / config
2KB 1.9ms 4.2ms 5.0ms 5.0ms Batch API response
4KB 2.5ms 4.2ms 5.0ms 5.0ms Document payload
8KB 3.7ms 4.3ms 5.1ms 5.1ms Rich API response

Node.js 22 • 200 iterations per size • Median send+receive roundtrip • 2-of-3 split

Fairness Disclosure
Competitor estimates use our measured primitive timings as reference—same algorithms (ML-KEM-768, X25519, AES-256), same JS runtime. Native implementations (CryptoKit, BoringSSL) would be faster for KEM operations, but Xlink’s Xchange eliminates KEM entirely—native KEM speed becomes irrelevant when the operation is removed from the pipeline.

Crypto Overhead as % of Total API Latency (256B)

Network Latency Xlink (Xchange) Tuta Signal
Local (1ms) 65.9% 80.8% 83.3%
Regional (10ms) 16.2% 29.6% 33.3%
Cross-region (50ms) 3.7% 7.7% 9.1%
Global (150ms) 1.3% 2.7% 3.2%

Xchange mode shown. Default split-channel overhead is higher (~9ms roundtrip). At 10ms+ latency, even split-channel overhead is modest.

Security vs Performance

Ranked by roundtrip speed at 256B. Xlink appears twice: Xchange (opt-in performance mode, single information-theoretic layer) and split-channel default (three independent layers including hybrid PQ KEM).

AES-256-GCM
Computational
256-bit symmetric key
Grover’s algorithm reduces effective key space to 128 bits
Security depends on AES not being broken
Key must be exchanged, stored, and protected
# System Roundtrip Security Model Channels PQ Protection
1 Xlink (Xchange) 1.9ms Information-theoretic k-of-n split Unconditional
2 Tuta 4.2ms Computational Single Kyber KEM only
3 Signal 5.0ms Hybrid computational Single Kyber KEM only
4 Apple PQ3 5.0ms Hybrid computational Single Kyber KEM only
5 Xlink (split-channel) ~9ms IT + computational (3 layers) k-of-n split KEM + opt-in ML-DSA
Key Insight
Every competitor sends the entire ciphertext through a single channel—compromise that channel and you have everything you need (ciphertext + metadata). Both Xlink modes split data across independent channels: an attacker must compromise k of n channels to reconstruct. Even Xchange (the fastest mode) is strictly more secure than any single-channel system because the split itself is information-theoretic—k−1 shares reveal zero bits regardless of compute power. Split-channel default adds hybrid PQ KEM + per-share encryption + dual signatures on top of this, creating three independent security layers.
Section 11

Honest Limitations

Eight known limitations documented transparently. No product is perfect — here is what Xlink does not do.

Limitation Impact Mitigation
Cleartext headers Sender/recipient DIDs, scope, timestamp visible to network observers. Traffic analysis possible. Payload is encrypted. Use TLS for transport-level protection of headers.
No push revocation Revoked agents' in-flight messages may be processed before receiver re-checks registry. Keep timestamp windows short (default 30s).
No key rotation Ed25519 identity keys are permanent. No built-in rotation protocol. Create new identity and re-register. Old DID becomes unused.
SHA-256 fallback When ECDH unavailable, shared key is deterministic. Not forward-secret. Ensure both parties publish X25519 keys for automatic ECDH upgrade.
Ephemeral nonce store MemoryNonceStore clears on process restart. Replays within timestamp window succeed. Use RedisNonceStore for production deployments.
Clock dependency Timestamp validation assumes synchronized clocks. Large skew causes false rejections. Use NTP. Increase timestampWindowMs for high-latency networks.
No payload size limits SDK does not enforce maximum payload size. Large payloads can exhaust memory. Validate payload size at application layer before calling send().
Registry trust Compromised registry can substitute public keys or modify scopes. Use createRegistryAuthMiddleware() with bearer token auth on writes.
Section 12

Post-Quantum Security

Xlink is end-to-end post-quantum. XorIDA payload splitting is information-theoretically quantum-safe. Key exchange uses hybrid X25519 + ML-KEM-768 (always-on, FIPS 203). Signatures use dual Ed25519 + ML-DSA-65 (opt-in via postQuantumSig: true, FIPS 204). All three cryptographic layers — payload, key exchange, and authentication — have quantum-safe implementations deployed.

Two Security Layers

The Xlink architecture has two distinct cryptographic layers with different quantum profiles:

LayerCurrentQuantum StatusUpgrade Path
Payload (XorIDA) GF(2) threshold sharing Quantum-safe now No change needed
Symmetric encryption AES-256-GCM Quantum-safe now No change needed
Key establishment X25519 + ML-KEM-768 (hybrid) Hybrid PQ — Phase 1 live Phase 1 deployed (v2 envelopes)
Identity / signatures Ed25519 + ML-DSA-65 (dual) Hybrid PQ — Phase 2 deployed (opt-in) Phase 2 deployed (v3 envelopes)

Migration Strategy

The upgrade follows a three-phase hybrid-first approach. Each phase maintains backward compatibility with the previous one.

PHASE 1 — DEPLOYED Key Exchange X25519 + ML-KEM-768 HKDF over both secrets Signatures Ed25519 (unchanged) Payload XorIDA (unchanged) Secure if either KEM holds PHASE 2 — DUAL SIG (DEPLOYED) Key Exchange X25519 + ML-KEM-768 HKDF over both secrets Signatures Ed25519 + ML-DSA-65 Both required to verify Payload XorIDA (unchanged) Full PQ + classical auth PHASE 3 — PURE PQ Key Exchange ML-KEM-768 only FIPS 203 standard Signatures ML-DSA-65 only FIPS 204 standard Payload XorIDA (unchanged) End-to-end post-quantum

Algorithm Profile

FunctionAlgorithmStandardKey / Sig Size
KEM (Phase 1+) ML-KEM-768 FIPS 203 1,184 B pub / 1,088 B ct
Hybrid KEM X25519 + ML-KEM-768 IETF draft HKDF over both shared secrets
Signature (Phase 2, deployed) ML-DSA-65 FIPS 204 1,952 B pub / 3,309 B sig
Symmetric AES-256-GCM NIST SP 800-38D Unchanged
Payload splitting XorIDA GF(2) Proprietary Unchanged

Latency Impact

Post-quantum operations add minimal computational overhead. The dominant latency remains network I/O, not cryptography.

OperationCurrent (Classical)Hybrid (Phase 1)Delta
Key exchange <0.1ms (X25519) ~0.3ms (X25519 + ML-KEM) +0.2ms
Sign <0.1ms (Ed25519) ~1.1ms (Ed25519 + ML-DSA) +1.0ms
Verify <0.2ms (Ed25519) ~0.7ms (Ed25519 + ML-DSA) +0.5ms
Full handshake ~0.4ms ~2.1ms +1.7ms
Envelope overhead ~128 bytes ~6,340 bytes +6.1 KB
Developer impact
The API surface does not change. Agent.create(), agent.send(), and agent.receive() remain identical. The post-quantum upgrade happens inside the Xlink infrastructure layer — all 68 ACIs inherit it automatically by updating one dependency.

Envelope Version

The envelope format uses a v2 version tag signaling hybrid PQ support. Agents negotiate capabilities automatically:

  • v1 agents communicate using classical crypto (X25519-only ECDH)
  • v2 agents communicate using hybrid PQ + classical crypto (X25519 + ML-KEM-768)
  • v2 agents automatically fall back to v1 when communicating with v1 peers
  • v3 agents add dual signatures (Ed25519 + ML-DSA-65) — deployed, opt-in via postQuantumSig: true
Phase 1 — deployed
Hybrid key establishment is live. v2 envelopes use X25519 + ML-KEM-768 (FIPS 203) with HKDF-SHA256 key combination. Session keys are secure as long as either scheme holds. Backward compatible — v1 agents work unchanged. The split-channel payload layer remains information-theoretically quantum-safe (XorIDA over GF(2)). Signatures: Phase 2 deployed — hybrid Ed25519 + ML-DSA-65 (opt-in via postQuantumSig: true).
Section 13

Protocol Security Stack

The Xlink protocol secures messages at three independent cryptographic layers. Each layer addresses a different threat surface. Together, they provide full-stack quantum safety with no single point of cryptographic failure.

Three-Layer Architecture

Layer Function Algorithm Standard Quantum Status
Payload Message confidentiality XorIDA threshold sharing over GF(2) Proprietary Immune
Key Exchange Session key establishment X25519 + ML-KEM-768 (hybrid) FIPS 203 Quantum-safe
Authentication Identity verification & integrity Ed25519 + ML-DSA-65 (dual) FIPS 204 Quantum-safe

Layer 1 — Payload (Information-Theoretic)

XorIDA splits the plaintext into threshold shares using XOR operations over GF(2). Each share is individually indistinguishable from random noise. No computation — classical or quantum — can extract information from fewer than k shares. This is not computational hardness; it is a mathematical proof. The payload layer is immune to harvest-now-decrypt-later attacks because there is no key to break, no structure to exploit, and no algorithm that reduces the problem.

Layer 2 — Key Exchange (Hybrid Post-Quantum KEM)

Session keys are established using a hybrid key encapsulation mechanism: classical X25519 ECDH combined with ML-KEM-768 (FIPS 203, formerly CRYSTALS-Kyber). Both shared secrets are combined via HKDF-SHA256 to derive the session key. The session key is secure as long as either X25519 or ML-KEM-768 remains unbroken — defense in depth. Hybrid KEM is live in v2 envelopes (Phase 1, deployed).

Layer 3 — Authentication (Dual Signatures)

Message authentication and sender identity use dual signatures: classical Ed25519 plus post-quantum ML-DSA-65 (FIPS 204). Both signatures must verify for the message to be accepted. ML-DSA-65 signatures are opt-in via postQuantumSig: true in the agent configuration. When enabled, v3 envelopes carry both signatures. Classical-only agents continue to work with v1/v2 envelopes.

Envelope Version Progression

  • v1 — Classical only. X25519 ECDH key exchange, Ed25519 signatures.
  • v2 — Hybrid PQ KEM. X25519 + ML-KEM-768 key exchange, Ed25519 signatures. Backward-compatible with v1 peers.
  • v3 — Full PQ. Hybrid KEM + dual signatures (Ed25519 + ML-DSA-65). Backward-compatible with v1/v2 peers. Default for split-channel.
  • Xchange — Opt-in performance mode. XorIDA key transport replaces KEM. Single security layer (information-theoretic) + Ed25519 authentication. ~180x faster than V3 split-channel. Activated explicitly via xchange: true.

V1, V2, V3 are the version progression. Xchange is a separate opt-in mode, not a version. Agents negotiate capabilities automatically. A v3 agent communicating with a v1 peer falls back to v1 envelope format. No developer action required — the protocol handles version negotiation internally.

Full-stack quantum safety available
With postQuantumSig: true, all three cryptographic layers are quantum-safe. The payload is information-theoretically immune. Key exchange uses hybrid PQ KEM (FIPS 203). Authentication uses dual PQ signatures (FIPS 204). All 68 ACIs inherit this protection by updating one dependency.
Section 14

Enterprise CLI

Self-hosted identity server. Docker-ready. Air-gapped capable. Port 3300. 73 tests. Part of the Enterprise CLI Suite.

@private.me/xlink-cli provides a production-grade identity management server with full REST API, agent lifecycle operations, trust registry management, and envelope processing. Integrates the complete @private.me/agent-sdk for enterprise M2M deployments.

CLI Commands

Xlink CLI
# Start the Xlink identity server
xlink serve --port 3300
  → HTTP server on :3300 with agent management + trust registry

# Create a new agent identity
xlink create --name "MyService"
  → Generates Ed25519 + X25519 keypairs, returns DID

# Send an encrypted envelope
xlink send --from "did:key:z6Mk..." --to "did:key:z6Mk..." --payload '{"action":"hello"}'
  → Creates v3 envelope, encrypts, signs, delivers

# Receive and decrypt an envelope
xlink receive --agent "did:key:z6Mk..." --envelope "base64-envelope"
  → Verifies signature, decrypts, validates nonce, returns payload

# Register a DID in trust registry
xlink register --did "did:key:z6Mk..." --name "Production API" --scope "api"
  → Adds to trust registry with scope-based permissions

# Revoke a compromised agent
xlink revoke --did "did:key:z6Mk..." --reason "key_compromise"
  → Marks DID as revoked, future envelopes rejected

# Query trust registry
xlink lookup --did "did:key:z6Mk..."
  → Returns registration status, scopes, metadata

Docker Deployment

Docker Compose
# Pull and run the Xlink identity server
docker compose up -d xlink

# Verify health
curl http://localhost:3300/health
# {"status":"ok","version":"0.1.0","uptime":42}

# Air-gapped deployment
docker save private.me/xlink-cli > xlink-cli.tar
# Transfer to air-gapped environment
docker load < xlink-cli.tar
docker compose up -d
3300
Default port
~15
REST endpoints
73
Tests passing
0
External deps
Enterprise CLI Suite
Xlink CLI is part of the Enterprise CLI Suite — 21 self-hosted reference servers covering identity management, passwordless auth, secret sharing, verifiable computation, encrypted search, secure ingest, key transport, split storage, and more. All Docker-ready, air-gapped capable, with HMAC-chained audit logs and multi-role RBAC.
Section 15

Migration from API Keys

Zero-downtime migration. Gradual traffic shifting. Version negotiation. DualModeAdapter for API key + Xlink hybrid deployments.

Migrating from API key-based authentication to Xlink cryptographic identity is a gradual process. The SDK provides a DualModeAdapter that handles both legacy API key requests and modern Xlink envelope requests simultaneously. Traffic shifts progressively from keys to identity over weeks or months, with zero service interruption.

DualModeAdapter Overview

The DualModeAdapter sits at your service boundary and inspects incoming requests. If the request contains a traditional API key (Authorization header, query parameter, or custom header), it routes to your existing authentication logic. If the request contains a Xlink envelope (Content-Type: application/xlink-envelope), it verifies the signature, checks the nonce, and extracts the payload.

DualModeAdapter setup
import { DualModeAdapter, Agent } from '@private.me/agent-sdk';
import express from 'express';

// Create Xlink agent for your service
const agent = (await Agent.create({
  name: 'ProductionAPI',
  registry, transport
})).value!;

// Initialize DualModeAdapter with legacy key validator
const adapter = new DualModeAdapter({
  agent,
  legacyKeyValidator: async (apiKey) => {
    // Your existing API key validation logic
    const user = await db.users.findOne({ apiKey });
    return user ? { userId: user.id, scopes: user.scopes } : null;
  },
  mode: 'hybrid',  // Accept both keys and Xlink envelopes
});

// Express middleware integration
const app = express();
app.use(adapter.middleware());

app.post('/api/data', async (req, res) => {
  // req.auth contains either legacy user data OR Xlink sender DID
  if (req.auth.mode === 'xlink') {
    console.log(`Request from Xlink DID: ${req.auth.did}`);
  } else {
    console.log(`Legacy API key user: ${req.auth.userId}`);
  }
  res.json({ status: 'ok' });
});
Gradual adoption, zero downtime
DualModeAdapter allows existing clients to continue using API keys while new clients adopt Xlink. No flag day. No forced upgrade. Clients migrate when ready. You monitor Xlink adoption rate via req.auth.mode metrics.

Traffic Shifting Strategy

Migration proceeds in three phases: hybrid mode (both keys and Xlink accepted), deprecation warnings (keys still work but clients receive upgrade prompts), and enforcement (keys rejected, Xlink required). The DualModeAdapter mode parameter controls this progression.

Phase Mode Setting API Key Requests Xlink Requests Duration
1. Hybrid hybrid Accepted Accepted 4-12 weeks
2. Deprecation deprecation Accepted + warning header Accepted 4-8 weeks
3. Enforcement xlink-only Rejected (401) Accepted Permanent
Progressive mode switching
// Week 0-12: Hybrid mode (both accepted)
adapter.setMode('hybrid');

// Week 12-20: Deprecation warnings
adapter.setMode('deprecation');
// API key requests receive X-Deprecated-Auth: "true" header
// Response includes upgrade instructions in X-Migration-Url

// Week 20+: Xlink enforcement
adapter.setMode('xlink-only');
// API key requests return 401 with migration guide URL

Monitor Xlink adoption via adapter.getMetrics(). Once 95%+ of traffic uses Xlink (typically 8-12 weeks in hybrid mode), activate deprecation warnings. After another 4-8 weeks, enforce Xlink-only mode. Adjust timelines based on your client base and communication channels.

Version Negotiation

Xlink envelope versions (v1, v2, v3, Xchange) auto-negotiate based on sender and receiver capabilities. Agents inspect the recipient's registered keys in the trust registry and select the highest mutually supported version. No manual configuration required.

Sender Capabilities Receiver Capabilities Negotiated Version Security Features
v3 (Ed25519 + X25519 + ML-KEM + ML-DSA) v3 (Ed25519 + X25519 + ML-KEM + ML-DSA) v3 Hybrid PQ KEM + dual signatures
v3 v2 (Ed25519 + X25519 + ML-KEM, no ML-DSA) v2 Hybrid PQ KEM + Ed25519 sig only
v3 v1 (Ed25519 + X25519, no PQ) v1 Classical crypto only
v3 + Xchange opt-in v3 + Xchange opt-in Xchange IT-secure split, no KEM (faster)
Version negotiation (automatic)
// Agent A: v3-capable (Ed25519 + X25519 + ML-KEM + ML-DSA)
const agentA = (await Agent.create({
  name: 'ServiceA',
  registry, transport,
  postQuantumSig: true,  // Enables ML-DSA-65
})).value!;

// Agent B: v2-capable (no ML-DSA support)
const agentB = (await Agent.create({
  name: 'ServiceB',
  registry, transport,
  postQuantumSig: false,  // Only Ed25519 signatures
})).value!;

// When A sends to B, SDK auto-negotiates to v2
await agentA.send({ to: agentB.did, payload: data });
// Uses v2 envelope (ML-KEM-768 + Ed25519, no ML-DSA)

// When A sends to another v3 agent, SDK uses v3
await agentA.send({ to: v3RecipientDid, payload: data });
// Uses v3 envelope (ML-KEM-768 + Ed25519 + ML-DSA-65)
Forward compatibility
Version negotiation is transparent to application code. When NIST finalizes CNSA 2.0 and the SDK adds ML-KEM-1024 + ML-DSA-87 support (v4), existing v3 agents automatically downgrade when talking to v3-only recipients. No code changes required.

Zero-Downtime Cutover Procedure

The recommended deployment pattern uses a phased rollout with canary testing and incremental traffic shifting. This procedure assumes a load-balanced multi-instance deployment behind a reverse proxy or API gateway.

Step 1: Canary Deployment (Week 0)

Deploy DualModeAdapter to a single instance (canary). Route 5% of traffic to it via load balancer weights. Monitor error rates, latency, and auth success metrics. If stable for 48 hours, proceed to Step 2.

Canary deployment
# Load balancer config (example: nginx upstream weights)
upstream api_backend {
  server instance1.internal:3000 weight=19;  # Legacy (95%)
  server instance2.internal:3000 weight=1;   # Canary DualMode (5%)
}

Step 2: Gradual Rollout (Week 1-4)

Deploy DualModeAdapter to all instances. Shift traffic incrementally: 10% → 25% → 50% → 75% → 100% over 4 weeks. Monitor Xlink adoption rate. Target: 30-50% of clients using Xlink envelopes by end of Week 4.

Incremental traffic shift
# Week 1: 10% DualMode
weight=18 (legacy), weight=2 (DualMode)

# Week 2: 25% DualMode
weight=15 (legacy), weight=5 (DualMode)

# Week 3: 50% DualMode
weight=10 (legacy), weight=10 (DualMode)

# Week 4: 100% DualMode (all instances)
all instances running DualModeAdapter in hybrid mode

Step 3: Client Migration (Week 5-12)

Publish Xlink integration guides and SDKs for your client ecosystem. Provide code examples, upgrade paths, and support channels. Track Xlink adoption via adapter.getMetrics(). Target: 80%+ adoption by end of Week 12.

Step 4: Deprecation Warnings (Week 13-20)

Switch DualModeAdapter to deprecation mode. API key requests still succeed but receive X-Deprecated-Auth: true header and migration guide URL. Monitor support tickets and client feedback. Extend timeline if needed.

Step 5: Enforcement (Week 21+)

Once 95%+ of traffic uses Xlink (typically Week 18-20), switch to xlink-only mode. API key requests return 401 Unauthorized with migration instructions. Maintain a support hotline for stragglers. After 4 weeks in enforcement mode, remove legacy API key validation code entirely.

Rollback safety
At any point before Step 5, you can roll back by switching DualModeAdapter back to hybrid mode or redeploying legacy instances. Keep legacy authentication logic intact until 4 weeks after enforcement begins. Have a tested rollback plan and practice it in staging.

Monitoring & Metrics

DualModeAdapter exposes real-time metrics for tracking migration progress and identifying adoption blockers.

Adapter metrics API
const metrics = adapter.getMetrics();

console.log(metrics);
// {
//   totalRequests: 142850,
//   xlinkRequests: 98420,
//   apiKeyRequests: 44430,
//   xlinkPercentage: 68.9,
//   failedAuth: 47,
//   avgLatencyMs: { xlink: 12.4, apiKey: 8.7 }
// }

// Export to monitoring system
setInterval(() => {
  const m = adapter.getMetrics();
  prometheus.gauge('xlink_adoption_pct').set(m.xlinkPercentage);
  prometheus.counter('xlink_requests_total').inc(m.xlinkRequests);
}, 60000);

Track the xlinkPercentage metric daily. A healthy migration shows steady week-over-week growth: 10% → 25% → 45% → 65% → 85% → 95%. If growth stalls, investigate client-side integration blockers (SDK confusion, documentation gaps, support tickets).

Advanced Reference

Implementation Details

Deep-dive into error handling, trust registry, nonce store, transport adapters, and the complete ACI surface.

Appendix A1

Error Hierarchy

Typed error classes for structured error handling. Supplementary to the Result<T,E> string code pattern.

Error class hierarchy
XlinkError                    // Base class (code, subCode, docUrl)
  ├── XlinkIdentityError       // Ed25519/X25519 keygen, sign, verify, DID
  ├── XlinkEnvelopeError       // Envelope create, encrypt, decrypt, parse
  ├── XlinkTransportError      // Send failures, network, timeouts
  ├── XlinkRegistryError       // Lookup, registration, revocation
  ├── XlinkKeyAgreementError   // X25519 ECDH derivation
  ├── XlinkSplitChannelError   // XorIDA split, reconstruct, HMAC
  └── XlinkAgentError          // High-level Agent lifecycle
Converting string codes to class errors
import { toXlinkError, isXlinkError } from '@private.me/agent-sdk';

const result = await agent.receive(envelope);
if (!result.ok) {
  const err = toXlinkError(result.error);
  console.log(err.code);     // 'DECRYPT_FAILED'
  console.log(err.subCode);  // 'KEY_AGREEMENT'
  console.log(err.docUrl);   // 'https://xail.io/docs/packages/xlink#envelope'
  console.log(err.name);     // 'XlinkEnvelopeError'
}
Appendix A2

Trust Registry

Four implementations covering development through production.

FileTrustRegistry
Production
JSONL-based persistent storage
Append-only log with in-memory replay
Single-node production deployments
HttpTrustRegistry
Production
HTTP-backed with per-DID cache
Configurable cache TTL (default 30s)
Multi-node distributed deployments
DidWebResolver
W3C
Resolves did:web via HTTPS
TLS mandatory (W3C spec)
5-minute cache TTL
Scope Authorization
RBAC
Per-agent scope sets
Checked on every receive()
SCOPE_DENIED on unauthorized

Production Persistence with FileTrustRegistry

For production single-node deployments, FileTrustRegistry provides persistent JSONL-based storage with automatic crash recovery. All operations append to an immutable log, replayed into memory on initialization.

FileTrustRegistry for production persistence
import { Agent, FileTrustRegistry, HttpsTransportAdapter } from '@private.me/agent-sdk';

// JSONL file persists across restarts
const registry = new FileTrustRegistry({ path: '/opt/app/trust.jsonl' });
const transport = new HttpsTransportAdapter({ baseUrl: 'https://api.example.com' });

const agent = (await Agent.create({ name: 'ProductionService', registry, transport })).value!;

// Registry automatically persists all add/update/remove operations
// Survives process restarts, crashes, and power failures

Enterprise Multi-Backend Factory

For enterprise deployments requiring centralized trust management with local fallback, createEnterpriseTrustRegistry() provides a factory function that combines HTTP primary + File fallback in a single interface.

Enterprise registry with automatic fallback
import { createEnterpriseTrustRegistry } from '@private.me/agent-sdk';

// Primary: centralized HTTP registry
// Fallback: local JSONL file when HTTP unreachable
const registry = createEnterpriseTrustRegistry({
  http: { baseUrl: 'https://trust.corp.example.com', authToken: process.env.TRUST_TOKEN },
  file: { path: '/var/lib/trust-fallback.jsonl' }
});

// Reads try HTTP first, fall back to file on network failure
// Writes go to both backends for redundancy
Agent resolve(did) hasScope(did, s) Trust Registry DID → Public Key DID → Scopes DID → X25519 Key + revocation status Result ok: rawPublicKey err: NOT_FOUND | REVOKED
Appendix A3

Nonce Store & Anti-Replay Protection

Replay prevention via unique nonce tracking. Every envelope carries a cryptographically random 16-byte nonce generated via crypto.getRandomValues().

Nonce-Based Replay Attack Prevention

A nonce (number used once) is a cryptographic value that ties each envelope to a single use. When an envelope arrives, the receiver checks whether its nonce has been seen before. If the nonce exists in the store, the envelope is rejected with REPLAY_DETECTED — preventing an attacker from capturing a valid envelope and re-sending it to execute the same action twice. This pattern is based on established cryptographic nonce practices used in OAuth 2.0, OIDC, and blockchain protocols.

Step Actor Action Defense
1. Generate Sender 16-byte nonce via crypto.getRandomValues() High-entropy random
2. Include Sender Nonce embedded in envelope (signed) Integrity-protected
3. Check Receiver NonceStore.check(nonce, senderDid) Atomic set-if-not-exists
4. Store Receiver Nonce stored with TTL expiry Time-bound memory usage
5. Replay Attacker Re-sends captured envelope Rejected (duplicate nonce)
Combined with timestamp validation
Nonces are scoped per sender DID and expire after the configured TTL (default: 10 minutes). Timestamp validation (default: 30-second window) provides a first line of defense against old messages. Nonce checks provide exact replay prevention within the TTL window. Together, they prevent both immediate replay and delayed replay attacks. See replay attack defense patterns for context.

Implementation Options

RedisNonceStore
Distributed
SET NX EX for atomic dedup
Cross-node replay prevention
Configurable TTL and key prefix
Bring your own Redis client
Distributed nonce store (Redis)
import { RedisNonceStore } from '@private.me/agent-sdk';
import Redis from 'ioredis';

const redis = new Redis({ host: 'redis.example.com' });

const nonceStore = new RedisNonceStore({
  client: redis,
  ttlSeconds: 600,        // 10 minutes (default)
  keyPrefix: 'nonce:',     // Redis key namespace
});

const agent = await Agent.create({
  name: 'DistributedAgent',
  registry, transport,
  nonceStore,              // Cross-node protection
});

Production deployments should use RedisNonceStore or an equivalent distributed store. Memory-based stores clear on process restart, allowing replay attacks within the timestamp window until the TTL expires. Redis provides atomic SET NX EX semantics, ensuring that even under high concurrency, a nonce is either accepted once or rejected as a duplicate — no race conditions.

Appendix A4

Transport Adapters

Pluggable delivery mechanism. Two built-in adapters plus a custom interface.

GatewayTransport
Xail Inbox
POST /gateway/deliver with API key
Optional polling for incoming
dispose() stops polling timer
Custom transport adapter interface
interface XailTransportAdapter {
  send(envelope: TransportEnvelope, to: string): Promise<Result<void, TransportError>>;
  onReceive(handler: (envelope: TransportEnvelope) => void): void;
  dispose(): void;
}
Appendix A5

Full ACI Surface

Complete Authenticated Cryptographic Interface organized by module.

Agent

Agent.create(opts) → Promise<Result<Agent>>

Generate identity, register with registry, wire transport. Primary factory.

Agent.fromIdentity(identity, opts) → Promise<Result<Agent>>

Restore from persisted PKCS8 identity. Skips keygen + registration.

Agent.fromParts(identity, registry, transport, opts?) → Agent

Synchronous construction from pre-built components. No async, no Result.

Agent.fromSeed(seed, opts) → Promise<Result<Agent>>

Deterministic identity from 32-byte seed via HKDF-SHA256. IoT factory provisioning.

agent.send({ to, payload, scope, action?, security? }) → Promise<Result<void>>

Encrypt, sign, and deliver. Auto ECDH when available. Automatic split-channel for high-risk operations.

agent.receive(envelope, { allowCleartext? }) → Promise<Result<AgentMessage>>

Verify version, timestamp, nonce, sender, signature, scope. Decrypt. Parse.

Identity

generateIdentity(opts?) → Promise<Result<AgentIdentity>>

Ed25519 + X25519 keypair generation via Web Crypto API. ML-DSA-65 keys when postQuantumSig: true.

exportPKCS8(key) / importFromPKCS8(bytes) / importIdentity(ed, x25519)

PKCS8 DER export/import for identity persistence.

identityFromSeed(seed) → Promise<Result<AgentIdentity>>

HKDF-SHA256 deterministic derivation from 32-byte seed.

Envelope

createEnvelope(opts) / createSignedEnvelope(opts)

Encrypted or signed-only envelope creation.

validateEnvelope(body) / deserializeEnvelope(data)

Validation and deserialization from unknown input.

Split-Channel

splitForChannel(plaintext, config?) → Promise<Result<ChannelShare[]>>

XorIDA split with HMAC integrity. Default: 3 shares, threshold 2.

reconstructFromChannel(shares) → Promise<Result<Uint8Array>>

Reconstruct from K+ shares. HMAC verified before returning plaintext.

Key Agreement

senderKeyAgreement(recipientPub) / receiverKeyAgreement(privKey, ephPub)

Hybrid X25519 + ML-KEM-768 key agreement with ephemeral key pair per message.

Appendix A6

Error Taxonomy

Complete error code table with sub-codes and error classes.

Code Class When
IDENTITY_FAILEDAgentIdentity generation fails during create()
REGISTRATION_FAILEDAgentRegistry rejects registration
REGISTRATION_FAILED:ALREADY_REGISTEREDAgentDID already exists in registry
VERIFICATION_FAILED:SIGNATURE_MISMATCHAgentEd25519 signature does not match
VERIFICATION_FAILED:DID_NOT_IN_REGISTRYAgentSender DID not found in registry
REPLAY_DETECTEDAgentDuplicate nonce (replay attack)
TIMESTAMP_EXPIREDAgentEnvelope outside timestamp window
SCOPE_DENIEDAgentSender lacks required scope
DECRYPT_FAILED:KEY_AGREEMENTEnvelopeECDH derivation fails
DECRYPT_FAILED:DECRYPTIONEnvelopeAES-GCM decryption fails
DECRYPT_FAILED:PARSEEnvelopeDecrypted bytes not valid JSON
SEND_FAILED:BELOW_THRESHOLDTransportSplit: fewer than K shares delivered
HMAC_VERIFICATION_FAILEDSplitChannelShare HMAC check fails
INSUFFICIENT_SHARESSplitChannelFewer than threshold shares
INCONSISTENT_SHARESSplitChannelMismatched groupId or params
INVALID_KEY_LENGTH:EXPECTED_32KeyAgreementX25519 key not 32 bytes
Appendix A7

Codebase Stats

Xlink v0.1.0 — Gold Standard Bronze tier achieved.

14
Source files
25
Test files
402
Tests passing
0
npm dependencies

Module Inventory

Module Source File Purpose
Identityidentity.tsEd25519 + X25519 keygen, DID, PKCS8, sign/verify
Envelopeenvelope.tsv1 create/decrypt/serialize/validate
Agentagent.tsTop-level ACI (create, send, receive, middleware)
Split-Channelsplit-channel.tsXorIDA bridge: split, reconstruct, HMAC
Key Agreementkey-agreement.tsX25519 ECDH ephemeral key agreement
Nonce Storenonce-store.tsMemoryNonceStore for replay prevention
Redis Nonceredis-nonce-store.tsRedisNonceStore for distributed deployments
Trust Registrytrust-registry.tsMemory + HTTP registry
DID:webdid-web.tsW3C did:web resolver
Transporttransport.tsInterface + HTTPS adapter
Gatewaygateway-transport.tsXail inbox gateway delivery
Middlewareregistry-middleware.tsExpress auth middleware for registry
Errorserrors.tsError class hierarchy (XlinkError + 7)
Verifyverify.tsLightweight verify-only sub-path

Deployment Options

🌐

SaaS Recommended

Fully managed infrastructure. Call our REST API, we handle scaling, updates, and operations.

  • Zero infrastructure setup
  • Automatic updates
  • 99.9% uptime SLA
  • Pay per use
View Pricing →
📦

SDK Integration

Embed directly in your application. Runs in your codebase with full programmatic control.

  • npm install @private.me/xlink
  • TypeScript/JavaScript SDK
  • Full source access
  • Enterprise support available
Get Started →
🏢

On-Premise Enterprise

Self-hosted infrastructure for air-gapped, compliance, or data residency requirements.

  • Complete data sovereignty
  • Air-gap capable
  • Docker + Kubernetes ready
  • RBAC + audit logs included
Enterprise CLI →