Client Setup

Installation

npm install ohttp-ts hpke

Basic Setup

import { OHTTPClient, KeyConfig } from 'ohttp-ts';
import { 
  CipherSuite, 
  KEM_DHKEM_X25519_HKDF_SHA256, 
  KDF_HKDF_SHA256, 
  AEAD_AES_128_GCM 
} from 'hpke';

// Create the cipher suite
const suite = new CipherSuite(
  KEM_DHKEM_X25519_HKDF_SHA256,
  KDF_HKDF_SHA256,
  AEAD_AES_128_GCM
);

// Fetch gateway's public key
const keyBytes = await fetch(
  'https://gateway.ohttp.info/.well-known/ohttp-gateway'
).then(r => r.arrayBuffer());

const keyConfig = KeyConfig.parse(new Uint8Array(keyBytes));

// Create the OHTTP client
const client = new OHTTPClient(suite, keyConfig);

Making Requests

Simple GET Request

const { request, context } = await client.encapsulateRequest(
  new Request('https://api.example.com/data'),
  'https://relay.ohttp.info/ohttp'
);

const encResponse = await fetch(request);
const response = await context.decapsulateResponse(encResponse);

console.log(await response.json());

POST with JSON Body

const { request, context } = await client.encapsulateRequest(
  new Request('https://api.example.com/submit', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name: 'Lilo', action: 'test' })
  }),
  'https://relay.ohttp.info/ohttp'
);

const encResponse = await fetch(request);
const response = await context.decapsulateResponse(encResponse);

Using Custom Relay/Gateway

You can use any OHTTP-compatible relay and gateway:

const RELAY_URL = 'https://my-relay.example.com/ohttp';
const GATEWAY_KEY_URL = 'https://my-gateway.example.com/.well-known/ohttp-gateway';

// Fetch custom gateway key
const keyBytes = await fetch(GATEWAY_KEY_URL)
  .then(r => r.arrayBuffer());
const keyConfig = KeyConfig.parse(new Uint8Array(keyBytes));

// Send through custom relay
const { request, context } = await client.encapsulateRequest(
  innerRequest,
  RELAY_URL
);

Error Handling

try {
  const { request, context } = await client.encapsulateRequest(
    innerRequest,
    relayUrl
  );
  
  const encResponse = await fetch(request);
  
  if (!encResponse.ok) {
    throw new Error(`Relay error: ${encResponse.status}`);
  }
  
  const response = await context.decapsulateResponse(encResponse);
  return response;
} catch (error) {
  if (error.message.includes('decapsulation')) {
    // Decryption failed - response may be corrupted or from wrong key
  }
  throw error;
}

Key Caching

For performance, cache the gateway’s key configuration:

let cachedKeyConfig: KeyConfig | null = null;
let cacheExpiry = 0;

async function getKeyConfig(): Promise<KeyConfig> {
  const now = Date.now();
  
  if (cachedKeyConfig && now < cacheExpiry) {
    return cachedKeyConfig;
  }
  
  const keyBytes = await fetch(
    'https://gateway.ohttp.info/.well-known/ohttp-gateway'
  ).then(r => r.arrayBuffer());
  
  cachedKeyConfig = KeyConfig.parse(new Uint8Array(keyBytes));
  cacheExpiry = now + 3600_000; // 1 hour
  
  return cachedKeyConfig;
}

Browser Usage

ohttp-ts works in browsers with WebCrypto support:

<script type="module">
  import { OHTTPClient, KeyConfig } from 'https://esm.sh/ohttp-ts';
  import { CipherSuite, ... } from 'https://esm.sh/hpke';
  
  // Same usage as Node.js
</script>

Cloudflare Workers

ohttp-ts is designed to work in Cloudflare Workers:

export default {
  async fetch(request: Request): Promise<Response> {
    const suite = new CipherSuite(...);
    const keyConfig = await getKeyConfig();
    const client = new OHTTPClient(suite, keyConfig);
    
    const { request: ohttpReq, context } = await client.encapsulateRequest(
      new Request('https://api.example.com/data'),
      'https://relay.ohttp.info/ohttp'
    );
    
    const encResponse = await fetch(ohttpReq);
    return context.decapsulateResponse(encResponse);
  }
};