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

API: Secure API Gateway

Third-party integrators need a secure, authenticated way to use XorIDA threshold sharing without managing cryptography. The API package provides cryptographic key management, multi-window rate limiting, and a split-channel service with organization isolation and permission-based access control.

v0.0.0 4 test suites Crypto deps SHA-256 hashed keys 3-tier rate limiting Result<T,E>
Section 01

Executive Summary

The API package wraps PRIVATE.ME's core XorIDA threshold sharing in an authenticated, rate-limited service layer for third-party integrators.

Every API key is SHA-256 hashed before storage. Keys are validated on every request, checked for expiration, and verified against permission scopes. Three-tier rate limiting enforces per-minute, per-hour, and per-day request quotas. The split-channel service automatically handles share creation, storage, and reconstruction with HMAC integrity verification.

Organizations are isolated: keys can only access share sets belonging to their own org. Maximum 10 shares per split operation. Threshold must be at least 2. All operations use the Result<T, E> pattern — no thrown exceptions.

This package exists to give integrators a clean, authenticated API surface without requiring them to manage cryptographic primitives directly. The underlying XorIDA operations are handled by @private.me/crypto.

Section 02

Developer Experience

API package provides structured error codes and clear validation feedback to help integrators build reliable services.

Structured Error Handling

Every operation returns a Result<T, E> with detailed error structures. Errors include machine-readable codes, human-readable messages, and actionable hints.

Error handling example
const result = await service.split(apiKey, {
  content: encoder.encode('Sensitive data'),
  threshold: 2,
  totalShares: 3,
  contentType: 'text/plain',
});

if (!result.ok) {
  switch (result.error.code) {
    case 'INVALID_API_KEY':
      // The API key format is invalid or not registered
      console.error('Authentication failed', result.error.message);
      break;
    case 'RATE_LIMITED':
      // One of the rate limit windows is exhausted
      console.error('Rate limit exceeded', result.error.message);
      break;
    case 'INSUFFICIENT_PERMISSIONS':
      // Key lacks required permission scope
      console.error('Permission denied', result.error.message);
      break;
  }
  return;
}

// Success: use result.value
const { uuid, shareIds } = result.value;

Error Categories

API package organizes errors into clear categories for systematic handling:

Code Category When
INVALID_API_KEY Authentication Key format invalid or not registered
KEY_EXPIRED Authentication Key has exceeded its TTL
RATE_LIMITED Rate Limiting Per-minute, per-hour, or per-day quota exhausted
INSUFFICIENT_PERMISSIONS Authorization Key lacks required permission scope
INVALID_REQUEST Validation Request parameters invalid (threshold, totalShares)
RETRIEVE_FAILED Reconstruction Share reconstruction or HMAC verification failed
Section 03

The Problem

Third-party integrators need threshold sharing but should not manage cryptographic primitives directly. Every organization that builds on XorIDA reinvents authentication, rate limiting, and permission management.

API Key Sprawl

Storing plaintext API keys in databases creates catastrophic breach risk. The traditional model of plaintext storage with database-level encryption still exposes keys to anyone with database access. Keys stored in logs, error messages, or accidentally committed to version control become permanent liabilities.

Rate Limiting Gaps

Without rate limiting, a compromised key can exhaust computational resources or create denial-of-service conditions. Single-window rate limiters (per-minute only) allow burst abuse patterns that evade detection. Organizations need multi-window protection: per-minute for burst prevention, per-hour for sustained abuse, per-day for long-term quota enforcement.

Permission Complexity

Integrators need granular permission scopes: some keys should only create shares, others should only retrieve, others should manage keys. Without permission enforcement at the service layer, every integration must implement its own authorization logic. This creates inconsistency and security gaps.

Organizational Isolation

A multi-tenant API must guarantee that Organization A cannot access Organization B's share sets. Without enforced isolation, a single compromised key exposes all stored data across all tenants. Traditional row-level security in databases is fragile and easy to bypass.

Critical Gap
No existing threshold sharing service provides cryptographic key hashing, multi-window rate limiting, permission-based access control, and organizational isolation in a single authenticated API layer.
Section 04

Use Cases

The API package serves as the foundation for third-party integrations that need authenticated threshold sharing.

🏦
Financial Services
Regulatory Document Splitting
Banks and financial institutions split SEC filings, audit reports, and compliance documents across geographically distributed storage. API keys are scoped to specific departments: compliance officers can create splits, auditors can retrieve, CFO can manage keys.
SOC 2 · FINRA · GDPR
🏥
Healthcare
PHI Distribution
Healthcare providers split protected health information across multiple HIPAA-compliant storage backends. Per-organization isolation ensures patient data from different hospitals never cross-contaminates. Rate limiting prevents accidental PHI bulk export.
HIPAA · BAA · 42 CFR Part 2
🛡️
Government / Defense
Classified Data Custody
Defense contractors split classified documents across air-gapped networks. API keys with share:create permission generate shares on a high-side network, keys with share:retrieve reconstruct on the low side after manual courier transfer.
FedRAMP · CMMC · FISMA
⚖️
Legal
Discovery Document Management
Law firms split privileged communications across multiple jurisdictions during international litigation. Per-organization isolation prevents cross-contamination between client matters. Permission scopes ensure paralegals can create splits but only attorneys can retrieve full documents.
Attorney-Client Privilege · eDiscovery
🏭
Manufacturing / IP
Trade Secret Distribution
Manufacturers split proprietary formulas and designs across contract manufacturers. Each supplier receives a subset of shares insufficient for reconstruction. Only the final assembly plant with all shares can reconstruct the complete design.
IP Protection · Supply Chain
🌐
SaaS Platforms
Multi-Tenant Data Segregation
SaaS providers offer threshold sharing as a feature to enterprise customers. Each customer organization gets isolated API keys and share sets. Rate limiting prevents one customer from exhausting shared compute resources. Permission scopes enable customer-specific RBAC.
Multi-Tenancy · SOC 2
Section 05

Architecture

The API package consists of three primary components: cryptographic key management, multi-window rate limiting, and an authenticated split-channel service.

API Key Management

API keys are generated with 32 bytes of cryptographic randomness via crypto.getRandomValues() and formatted with the xail_ prefix (64 hex characters). Only the SHA-256 hash of each key is stored. The plaintext key is shown once at creation and never persisted.

Key creation
import { ApiKeyManager, DEFAULT_RATE_LIMIT } from '@private.me/api';

const keyManager = new ApiKeyManager();

const result = await keyManager.createKey(
  'org-bank-123',
  'Production Key',
  ['share:create', 'share:retrieve'],
  DEFAULT_RATE_LIMIT,
  365 * 24 * 60 * 60 * 1000 // 1 year TTL
);

if (!result.ok) {
  console.error(result.error.message);
  return;
}

// Show the key once and never again
const { keyString, key } = result.value;
console.log('API Key:', keyString);
console.log('Key ID:', key.id);
console.log('Store this key securely. It will not be shown again.');

Keys have configurable TTL (default: 1 year). Expired keys are rejected at validation time. Keys can be revoked immediately via revokeKey(keyId).

Multi-Window Rate Limiting

The rate limiter enforces three independent token bucket windows: per-minute, per-hour, and per-day. A request is rejected if any of the three windows is exhausted. Buckets auto-refill when their time window elapses.

Rate limiter setup
import { RateLimiter, DEFAULT_RATE_LIMIT } from '@private.me/api';

const rateLimiter = new RateLimiter();

// Register limits for a key (called after key creation)
rateLimiter.register(key.id, DEFAULT_RATE_LIMIT);

// Check and consume a token on each request
const result = rateLimiter.consume(key.id);
if (!result.ok) {
  console.error('Rate limited:', result.error.message);
  return;
}

// Get remaining tokens per window
const remaining = rateLimiter.getRemaining(key.id);
console.log('Remaining:', remaining);
// { minute: 59, hour: 999, day: 9999 }

Default limits: 60 requests/minute, 1000 requests/hour, 10000 requests/day. Custom limits can be configured per key at creation time.

Split-Channel Service

The split-channel service wraps @private.me/crypto's XorIDA operations with authentication, rate limiting, and HMAC integrity verification. Every request validates the API key, checks expiration, verifies permissions, and consumes a rate limit token before processing.

Split and retrieve
import { SplitChannelService } from '@private.me/api';

const service = new SplitChannelService(keyManager, rateLimiter);

// Split content into threshold shares
const encoder = new TextEncoder();
const content = encoder.encode('Sensitive document content');

const splitResult = await service.split(apiKey, {
  content,
  threshold: 2,
  totalShares: 3,
  contentType: 'text/plain',
});

if (!splitResult.ok) {
  console.error(splitResult.error.message);
  return;
}

const { uuid, shareIds } = splitResult.value;
console.log('Share set UUID:', uuid);
console.log('Share IDs:', shareIds);

// Later: retrieve content from shares
const retrieveResult = await service.retrieve(apiKey, {
  uuid,
  shareIndices: [1, 2], // Any 2 of 3 shares
});

if (!retrieveResult.ok) {
  console.error(retrieveResult.error.message);
  return;
}

const { content: reconstructed, contentType } = retrieveResult.value;
const decoder = new TextDecoder();
console.log('Reconstructed:', decoder.decode(reconstructed));

Share sets are stored in memory indexed by UUID and organization ID. Only keys belonging to the same organization can access a share set. HMAC verification runs before reconstruction — if any share is tampered with, the operation fails closed.

Organizational Isolation
Every share set is tagged with the organization ID from the creating key. Retrieval requests validate that the requesting key belongs to the same organization. This guarantees multi-tenant isolation at the service layer.
Section 06

Integration

The API package is designed for use in backend services. All state is held in memory. For production deployment, implement persistent storage adapters.

Installation

Install via pnpm
pnpm add @private.me/api

Dependencies: @private.me/shared (types), @private.me/crypto (XorIDA operations).

Complete Example

End-to-end integration
import {
  ApiKeyManager,
  RateLimiter,
  SplitChannelService,
  DEFAULT_RATE_LIMIT,
} from '@private.me/api';

// Initialize components
const keyManager = new ApiKeyManager();
const rateLimiter = new RateLimiter();
const service = new SplitChannelService(keyManager, rateLimiter);

// Create an API key for an organization
const keyResult = await keyManager.createKey(
  'org-acme-corp',
  'Production API Key',
  ['share:create', 'share:retrieve'],
  DEFAULT_RATE_LIMIT
);

if (!keyResult.ok) throw new Error(keyResult.error.message);

const { keyString, key } = keyResult.value;
rateLimiter.register(key.id, DEFAULT_RATE_LIMIT);

// Use the key to split content
const encoder = new TextEncoder();
const content = encoder.encode('Confidential report');

const splitResult = await service.split(keyString, {
  content,
  threshold: 2,
  totalShares: 3,
  contentType: 'text/plain',
});

if (!splitResult.ok) throw new Error(splitResult.error.message);

console.log('Share set created:', splitResult.value.uuid);

// Later: retrieve using the same key
const retrieveResult = await service.retrieve(keyString, {
  uuid: splitResult.value.uuid,
  shareIndices: [1, 2],
});

if (!retrieveResult.ok) throw new Error(retrieveResult.error.message);

const decoder = new TextDecoder();
console.log('Reconstructed:', decoder.decode(retrieveResult.value.content));

Persistent Storage Adapter Pattern

For production deployments, wrap the API components with persistent storage adapters. The in-memory implementation is designed to be easily replaceable.

Storage adapter interface
// Example: PostgreSQL adapter for ApiKeyManager
interface ApiKeyStore {
  save(key: ApiKey): Promise<void>;
  findByHash(keyHash: string): Promise<ApiKey | null>;
  listByOrg(orgId: string): Promise<ApiKey[]>;
  revoke(keyId: string): Promise<boolean>;
}

// Example: Redis adapter for share sets
interface ShareSetStore {
  save(orgId: string, uuid: string, shareSet: SplitResponse): Promise<void>;
  findByUuid(orgId: string, uuid: string): Promise<SplitResponse | null>;
  listByOrg(orgId: string): Promise<SplitResponse[]>;
}
Section 07

Security

The API package enforces defense-in-depth: cryptographic key hashing, multi-window rate limiting, permission-based authorization, and organizational isolation.

API Key Security

Keys are generated with crypto.getRandomValues() (32 bytes = 256 bits of entropy). Only the SHA-256 hash is stored. The plaintext key is returned once at creation and never persisted anywhere in the system.

Cryptographic Hashing
Even if the database is compromised, attackers gain only SHA-256 hashes. Reversing a SHA-256 hash to recover the original key is computationally infeasible (2256 brute-force attempts).

Rate Limiting Defense

Three-tier rate limiting prevents both burst abuse and sustained attacks:

  • Per-minute: Prevents burst abuse (default: 60 req/min)
  • Per-hour: Prevents sustained attack patterns (default: 1000 req/hr)
  • Per-day: Enforces long-term quotas (default: 10000 req/day)

A request is rejected if any of the three windows is exhausted. This prevents attackers from evading detection by spacing out requests just enough to pass a single-window limiter.

Permission-Based Authorization

Every API key has a list of permission scopes. The service validates permissions before processing each request:

Permission Operations Allowed
share:create Create new share sets via split()
share:retrieve Reconstruct content via retrieve()
share:list List all share sets via listShareSets()
key:manage Create, revoke, and list API keys (admin only)

Organizational Isolation

Every API key is tagged with an organization ID. Every share set is tagged with the org ID of the key that created it. Retrieval requests validate that the requesting key belongs to the same org as the share set.

This guarantees multi-tenant isolation: Organization A cannot access Organization B's shares, even if A guesses B's share set UUID.

HMAC Integrity Verification

Every share includes an HMAC-SHA256 tag. Reconstruction verifies HMAC integrity before returning data. If any share is tampered with, the operation fails closed — no partial data leakage.

Fail-Closed Security
If HMAC verification fails during reconstruction, the service returns RETRIEVE_FAILED and logs the failure. The system never returns potentially corrupted data.
Section 08

Performance

The API package introduces minimal overhead beyond the underlying XorIDA operations from @private.me/crypto.

Operation Overhead

<1ms
Key validation
<0.5ms
Rate limit check
<0.3ms
Permission check

Total authentication and authorization overhead: <2ms per request. The majority of request time is spent in XorIDA operations (splitting or reconstruction), not in the API layer.

Memory Footprint

In-memory storage is lightweight:

  • API keys: ~200 bytes per key (hash, metadata, permissions)
  • Rate limit buckets: ~80 bytes per key (3 windows × timestamps + counters)
  • Share sets: Variable (depends on content size + number of shares)

For 10,000 API keys with active rate limit tracking: ~2.8 MB memory footprint (excluding share sets).

Throughput

Single-threaded throughput on a modern CPU (Apple M1):

  • Key validation: ~50,000 ops/sec
  • Rate limit checks: ~120,000 ops/sec
  • Permission checks: ~200,000 ops/sec

The API layer is not the bottleneck. XorIDA splitting and reconstruction dominate request latency.

Section 09

Limitations

The API package is designed for backend services with single-process deployments. Multi-process and distributed deployments require persistent storage adapters.

In-Memory State

All state (API keys, rate limit buckets, share sets) is held in memory. If the process restarts, all state is lost. For production deployments:

  • Implement persistent storage adapters for API keys (e.g., PostgreSQL)
  • Use distributed cache for rate limit state (e.g., Redis)
  • Store share sets in durable storage (e.g., S3, database)

Single-Process Rate Limiting

Rate limiting state is not shared across processes. In a multi-process deployment (e.g., load-balanced instances), each process maintains independent rate limit buckets. A key with a 60 req/min limit could make 60 req/min to each process.

Solution: Use a distributed rate limiting backend (Redis, Memcached) for multi-process deployments.

No Auto-Expiration

The ApiKeyManager checks TTL per-operation but does not auto-purge expired keys. The RateLimiter refills buckets automatically but does not remove entries for revoked keys. The SplitChannelService stores share sets indefinitely.

For long-running servers, implement periodic cleanup:

Cleanup pattern
// Periodically purge expired keys
setInterval(() => {
  const allKeys = keyManager.listKeys('org-id');
  for (const key of allKeys) {
    if (Date.now() > key.expiresAt) {
      keyManager.revokeKey(key.id);
      rateLimiter.remove(key.id);
    }
  }
}, 60 * 60 * 1000); // Every hour

Maximum Share Limits

The service enforces a maximum of 10 shares per split operation. Threshold must be at least 2. These limits are enforced at the service layer and cannot be overridden.

Honest Limitation
This package is not designed for distributed deployments out of the box. It requires persistent storage adapters and distributed rate limiting backends for production use at scale.
Advanced Topics

Implementation Details

Deep dive into memory management, permission scopes, API surface, and error handling.

Advanced 01

Memory Management

The API package uses in-memory Maps for all state. Understand the lifecycle and cleanup requirements.

State Storage

Three independent Map instances hold state:

  • ApiKeyManager: Map<string, ApiKey> (key hash → key record)
  • RateLimiter: Map<string, BucketState> (key ID → bucket state)
  • SplitChannelService: Map<string, SplitResponse> (UUID → share set)

Lifecycle Management

API keys are created via createKey() and revoked via revokeKey(). Revoked keys remain in the Map but are marked inactive. They are rejected at validation time.

Rate limit buckets are created via register() and removed via remove(). When a key is revoked, call remove(keyId) to free memory.

Share sets are created via split() and stored indefinitely. There is no built-in expiration. Implement application-level TTL if shares should expire.

Cleanup Strategy

Production cleanup pattern
// Run cleanup every hour
setInterval(() => {
  // 1. Find and revoke expired keys
  const orgs = ['org-1', 'org-2']; // All orgs in your system
  for (const orgId of orgs) {
    const keys = keyManager.listKeys(orgId);
    for (const key of keys) {
      if (Date.now() > key.expiresAt) {
        keyManager.revokeKey(key.id);
        rateLimiter.remove(key.id);
      }
    }
  }

  // 2. Remove old share sets (example: 30-day TTL)
  const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
  for (const orgId of orgs) {
    const shareSets = await service.listShareSets(adminKey);
    for (const shareSet of shareSets.value) {
      if (shareSet.createdAt < cutoff) {
        // Remove from internal Map (requires exposing a delete method)
      }
    }
  }
}, 60 * 60 * 1000);
Advanced 02

Permission Scopes

The API package enforces least-privilege access control via permission scopes.

Scope Definitions

Scope Operations Typical Use
share:create split() Application servers that create share sets
share:retrieve retrieve() Application servers that reconstruct content
share:list listShareSets() Admin dashboards, audit tools
key:manage createKey(), revokeKey(), listKeys() Admin users, key rotation tools

Permission Patterns

Read-Only Key
Audit
share:retrieve
share:list
Use: Compliance audit tools
Admin Key
Restricted
key:manage
share:* (all share scopes)
Use: Platform operators only
Service Key
Standard
share:create
share:retrieve
Use: Application backends
Advanced 03

Full ACI Surface

Complete API reference for all exported functions and classes.

API Key Management

generateKeyString(): string
Generate a random API key with xail_ prefix + 64 hex chars (32 bytes entropy).
hashApiKey(keyString: string): Promise<string>
SHA-256 hash for storage. Returns hex-encoded hash.
isValidKeyFormat(keyString: string): boolean
Validate key format: xail_ prefix + 64 hex chars.
ApiKeyManager.createKey(orgId, name, permissions, rateLimit?, ttlMs?)
Create a new API key. Returns Result<{keyString, key}, ApiError>. Default TTL: 1 year.
ApiKeyManager.validateKey(keyString): Promise<Result<ApiKey, ApiError>>
Validate and return the key record. Checks format, hash, expiration, and active status.
ApiKeyManager.hasPermission(key, permission): boolean
Check if a key has a specific permission scope.
ApiKeyManager.revokeKey(keyId): boolean
Deactivate a key by ID. Returns true if revoked, false if not found.
ApiKeyManager.listKeys(orgId): readonly ApiKey[]
List all keys for an organization (both active and revoked).

Rate Limiting

RateLimiter.register(keyId, limits): void
Register rate limits for a key. Call after key creation.
RateLimiter.consume(keyId): Result<true, ApiError>
Check and consume a token. Returns RATE_LIMITED error if any window exhausted.
RateLimiter.getRemaining(keyId): {minute, hour, day} | null
Get remaining tokens per window. Returns null if key not registered.
RateLimiter.remove(keyId): boolean
Remove rate limit state. Call when revoking a key to free memory.

Split-Channel Service

SplitChannelService.split(keyString, request): Promise<Result<SplitResponse, ApiError>>
Split content into threshold shares. Validates key, checks permissions, consumes rate limit token, creates shares via XorIDA.
SplitChannelService.retrieve(keyString, request): Promise<Result<RetrieveResponse, ApiError>>
Reconstruct content from shares. Validates key, checks permissions, verifies HMAC, reconstructs via XorIDA.
SplitChannelService.listShareSets(keyString): Promise<Result<readonly SplitResponse[], ApiError>>
List all share sets for the organization. Requires share:list permission.
Advanced 04

Error Reference

Complete error code taxonomy with descriptions and recovery hints.

Code Category Description
INVALID_API_KEY Authentication The API key format is invalid or the key is not registered. Verify the key string matches the format xail_[64 hex chars].
KEY_EXPIRED Authentication The API key has exceeded its TTL. Create a new key via createKey().
RATE_LIMITED Rate Limiting One of the three rate limit windows (per-minute, per-hour, per-day) is exhausted. Wait for the window to reset. Use getRemaining() to check quotas.
INSUFFICIENT_PERMISSIONS Authorization The API key does not have the required permission scope for this operation. Check the key's permissions array.
INVALID_REQUEST Validation Request parameters are invalid. Common causes: threshold < 2, totalShares > 10, threshold > totalShares, or invalid share indices.
RETRIEVE_FAILED Reconstruction Share reconstruction failed. Shares may be corrupted or the HMAC integrity check did not pass. Verify shares were not tampered with.

Error Handling Best Practices

Comprehensive error handling
const result = await service.split(apiKey, request);

if (!result.ok) {
  const { code, message } = result.error;

  switch (code) {
    case 'INVALID_API_KEY':
      // Log security event, reject request
      logger.security('Invalid API key attempt', { keyPrefix });
      return { status: 401, error: 'Unauthorized' };

    case 'KEY_EXPIRED':
      // Notify user, trigger key rotation flow
      notifyAdmin('API key expired, rotation required');
      return { status: 401, error: 'Key expired' };

    case 'RATE_LIMITED':
      // Return 429 with Retry-After header
      const remaining = rateLimiter.getRemaining(keyId);
      return {
        status: 429,
        error: 'Rate limit exceeded',
        retryAfter: remaining?.minute === 0 ? 60 : 3600
      };

    case 'INSUFFICIENT_PERMISSIONS':
      // Log authorization failure
      logger.warn('Permission denied', { keyId, operation: 'split' });
      return { status: 403, error: 'Forbidden' };

    case 'INVALID_REQUEST':
      // Return 400 with validation details
      return { status: 400, error: message };

    case 'RETRIEVE_FAILED':
      // Log integrity failure, potential tampering alert
      logger.security('HMAC verification failed', { uuid });
      return { status: 500, error: 'Reconstruction failed' };

    default:
      // Unexpected error, log and return 500
      logger.error('Unexpected API error', { code, message });
      return { status: 500, error: 'Internal server error' };
  }
}
Deployment

Deployment

The API package is available upon request for enterprise integrators requiring an authenticated API layer over XorIDA threshold sharing.

Upon Request
This package is distributed on a case-by-case basis to qualified enterprise integrators. It is not part of the standard SDK distribution. Contact sales for access and licensing terms.

Integration Requirements

Package installation (after approval)
# Add to package.json dependencies
{
  "dependencies": {
    "@private.me/api": "^0.1.0"
  }
}

# Install via private registry
pnpm install

Environment Configuration

The API package requires minimal configuration. All cryptographic operations are handled internally by @private.me/crypto.

Example .env configuration
# API service configuration
API_PORT=8080
API_LOG_LEVEL="info"

# Rate limiting configuration (requests per window)
RATE_LIMIT_MINUTE=60
RATE_LIMIT_HOUR=1000
RATE_LIMIT_DAY=10000

# API key TTL (in milliseconds)
API_KEY_TTL=86400000  # 24 hours

Production Deployment

The API package is designed for server-side integration. It is not a standalone CLI or HTTP service — it is a TypeScript library that your application imports and uses.

Server integration example
import { createSplitChannelService, createKeyManager } from '@private.me/api';

// Initialize services
const keyManager = createKeyManager();
const splitService = createSplitChannelService();

// Create API key for a new org
const keyResult = await keyManager.createKey({
  orgId: 'org-12345',
  permissions: ['split', 'retrieve'],
  ttl: 86400000 // 24 hours
});

if (!keyResult.ok) {
  throw new Error(keyResult.error.message);
}

// Use in HTTP handler
app.post('/api/split', async (req, res) => {
  const apiKey = req.headers['x-api-key'];
  const result = await splitService.split(apiKey, req.body);

  if (!result.ok) {
    return res.status(400).json({ error: result.error });
  }

  res.json(result.value);
});

Security Best Practices

  • API Key Rotation: Rotate keys regularly using the ttl parameter. Monitor expiration and notify clients before keys expire.
  • Rate Limit Tuning: Adjust rate limits based on your integration's traffic patterns. Default limits are conservative.
  • Scope Isolation: Use permission scopes to limit key capabilities. Grant only the minimum required permissions.
  • Organization Boundaries: Every API key is bound to an orgId. Keys cannot access shares from other organizations.
  • HMAC Verification: All shares include HMAC integrity checks. Failed verification indicates tampering — log and alert immediately.
  • Transport Security: Always use HTTPS/TLS when transmitting API keys and shares. Never expose keys in URLs or logs.
Enterprise Support
Approved integrators receive dedicated support for deployment, monitoring, and security reviews. Contact your PRIVATE.ME account manager for deployment assistance.