npm.io
1.2.1 • Published 2 weeks ago

tinfoil

Licence
Apache-2.0
Version
1.2.1
Deps
7
Size
189 kB
Vulns
0
Weekly
0
Stars
12

Tinfoil TypeScript SDK

Build Status NPM version Documentation

A TypeScript client for verifiably private AI inference with the Tinfoil API. Supports the OpenAI API format and the Vercel AI SDK.

Tinfoil runs LLMs inside secure enclaves—isolated environments on hardware where even Tinfoil cannot access your data. This SDK encrypts your requests using HPKE (RFC 9180) via the EHBP protocol, so that only the verified enclave can decrypt them.

It also supports TLS certificate pinning as an alternative transport mode, where all connections are encrypted and terminated to a verified secure enclave.

Installation

npm install tinfoil

Requires Node 20+. Works in browsers with ES2020 support, Electron, and Bun (see Bun Support).

Quick Start

You'll need an API key to get started.

import { TinfoilAI } from "tinfoil";

const client = new TinfoilAI({
  apiKey: "<YOUR_API_KEY>", // or use TINFOIL_API_KEY env var
});

const completion = await client.chat.completions.create({
  messages: [{ role: "user", content: "Hello!" }],
  model: "llama3-3-70b", // See all models: https://docs.tinfoil.sh/models/catalog
});

Browser Usage

Use bearerToken for browser authentication (e.g., JWT from your auth system):

import { TinfoilAI } from "tinfoil";

const client = new TinfoilAI({
  bearerToken: "your-jwt-token", // From your auth system
});

await client.ready(); // Wait for verification to complete

const completion = await client.chat.completions.create({
  model: "llama3-3-70b",
  messages: [{ role: "user", content: "Hello!" }],
});

Warning: Never use apiKey in browser code—it exposes your key in page source. Use bearerToken with your backend authentication. If you must use apiKey instead of bearerToken in the browser, set dangerouslyAllowBrowser: true.

Using with OpenAI SDK

If you prefer the OpenAI SDK directly, use SecureClient to get a verified fetch function:

import OpenAI from "openai";
import { SecureClient } from "tinfoil";

const secureClient = new SecureClient();
await secureClient.ready();

const openai = new OpenAI({
  apiKey: "<YOUR_API_KEY>",
  baseURL: secureClient.getBaseURL(),
  fetch: secureClient.fetch,
});

const completion = await openai.chat.completions.create({
  model: "llama3-3-70b",
  messages: [{ role: "user", content: "Hello!" }],
});

Realtime API (WebSockets)

Open a realtime WebSocket session (e.g. streaming speech-to-text) with the connection pinned to the attested enclave key. Returns an OpenAIRealtimeWS client from the OpenAI SDK:

import { TinfoilAI } from "tinfoil";

const client = new TinfoilAI({ apiKey: "<YOUR_API_KEY>" });

const rt = await client.realtime({ model: "voxtral-mini-4b-realtime" });

rt.on("session.created", (event) => console.log(event));
rt.send({ type: "input_audio_buffer.append", audio: base64AudioChunk });

For lower-level control, SecureClient.createWebSocket() opens a pinned ws socket to any enclave path, and SecureClient.getPinnedWebSocketOptions() returns pinning options for wiring into other WebSocket-based libraries.

WebSocket frames are not covered by EHBP (which seals HTTP bodies), so realtime connections go directly to the enclave over TLS pinned to the attested key — the same guarantee as the tls transport mode. Node.js only: browsers cannot pin TLS connections, and a proxy baseURL cannot present the enclave's certificate (both are rejected with a ConfigurationError).

Using with Vercel AI SDK

Full support for the Vercel AI SDK in both server and browser environments.

AI Server SDK (Node.js / Next.js)

Use createTinfoilAI for server-side AI SDK functions:

import { createTinfoilAI } from "tinfoil";
import { generateText, streamText } from "ai";

const tinfoil = await createTinfoilAI("<YOUR_API_KEY>");

const { text } = await generateText({
  model: tinfoil("llama3-3-70b"),
  prompt: "Hello!",
});

For Next.js API routes, initialize once at module level to avoid repeated verification:

// app/api/chat/route.ts
const tinfoilPromise = createTinfoilAI(process.env.TINFOIL_API_KEY!);

export async function POST(req: Request) {
  const tinfoil = await tinfoilPromise;
  // ...
}

See the Vercel AI Server SDK Example for more details.

AI Browser SDK (React / Vue / etc.)

For browser apps, use SecureClient with DefaultChatTransport. A proxy server is required to keep your API key secret.

import { SecureClient } from "tinfoil";
import { DefaultChatTransport } from "ai";

const secureClient = new SecureClient({
  baseURL: "https://your-proxy.com/",
});
await secureClient.ready(); // Wait for attestation

const transport = new DefaultChatTransport({
  api: "/v1/chat/completions",
  fetch: secureClient.fetch,
});

See the Vercel AI Browser SDK Example for complete React patterns with useChat, context providers, and error handling.

Prompt Cache Scoping

The inference router partitions prompt-prefix caches using both the authenticated API identity and user_cache_secret. Cache reuse requires the same identity, secret, model, and matching prompt prefix. Changing the identity or secret selects a different cache namespace, so those requests do not share cache entries or cache-hit timing.

user_cache_secret is sensitive application data used only for cache partitioning. It is not an API credential or encryption key. Do not log or expose it unnecessarily: a caller who can send requests with the same API identity and secret joins that cache namespace and can observe its cache-hit timing. The SDK adds it to eligible request bodies before they are protected for transport to the verified enclave.

By default, Node.js attempts to generate a random secret and persists it at ~/.tinfoil/user_cache_secret, requesting mode 0600 where supported. Tinfoil SDKs using the same home directory reuse this value. Browsers attempt to use a runtime-lifetime value instead. This default is suitable for a single-user application, but it does not separate end users who share one application process, runtime, or home directory. You can control the scope explicitly:

// Pin a stable, non-empty, opaque secret for this client.
// SecureClient and createTinfoilAI accept the same option.
const client = new TinfoilAI({ userCacheSecret: secret });

// Or provision it via the environment (Node.js)
//   TINFOIL_USER_CACHE_SECRET=<secret>   use this value

// Multi-user services should scope every request to its end user;
// a non-empty string `user_cache_secret` field set in the request body wins
// over the client-level secret:
const completion = await client.chat.completions.create({
  model: "llama3-3-70b",
  messages: [{ role: "user", content: "Hello!" }],
  user_cache_secret: perUserSecret,
} as TinfoilAI.Chat.ChatCompletionCreateParams);

Resolution order is a non-empty per-request string, a non-empty client value, a non-empty TINFOIL_USER_CACHE_SECRET, then an attempted generated default. Empty client or environment values are treated as unset, and an empty per-request string is replaced with the resolved client value. The SDK leaves non-string values unchanged, and applications should not use them for cache scoping.

Multi-user services must provide a stable, non-empty, opaque value for each user (or group whose members may share cache-hit timing) on every eligible request. Do not use a raw user identifier, API key, or encryption key. A single client, environment, or generated value groups all requests using it under the same API identity. If persistence is unavailable and secure random generation succeeds, the SDK uses an in-memory value and cache continuity ends when that process or runtime exits. If secure random generation also fails, automatic prompt-cache scoping is unavailable.

How Verification Works

When you create a client, the SDK automatically:

  1. Verifies the enclave — Fetches attestation and checks AMD SEV-SNP hardware signatures to prove it's a genuine secure enclave
  2. Verifies the code — Confirms the running code matches the signed GitHub release (via Sigstore)
  3. Establishes encryption — Creates an encrypted connection that only the verified enclave can decrypt

Your requests are encrypted before leaving your machine. Even Tinfoil cannot read them—only the verified enclave can decrypt and process your data.

Transport Modes:
  • HPKE (default): End-to-end encrypted via RFC 9180, works through proxies
  • TLS Pinning: Direct TLS certificate pinning to the enclave (requires direct connection, no proxy support)

For a deeper understanding, see How It Works, Confidentiality, Verifiability and Attestation Architecture.

Verification API

The Verifier class is for advanced use cases where you want to verify an enclave before creating a client, or verify arbitrary enclaves independently.

import { Verifier } from "tinfoil";

const verifier = new Verifier({
  serverURL: "https://enclave.host.com",
  configRepo: "tinfoilsh/confidential-model-router",
});

const attestation = await verifier.verify();
console.log(attestation.tlsPublicKeyFingerprint);
console.log(attestation.hpkePublicKey);

const doc = verifier.getVerificationDocument();
console.log(doc.securityVerified);
console.log(doc.steps); // fetchDigest, verifyCode, verifyEnclave, compareMeasurements

Proxy Support

Route requests through your own backend while keeping request bodies encrypted end-to-end. This lets you:

  • Keep API keys on your server
  • Add authentication, rate limiting, logging
  • The proxy sees headers/URLs but cannot decrypt request or response bodies
import { SecureClient } from "tinfoil";

const client = new SecureClient({
  baseURL: "https://your-proxy-server.com/",
});

await client.ready();
// Requests go to your proxy, bodies remain encrypted to the enclave

For full proxy server implementation (Go example, CORS config, header handling), see the Encrypted Request Proxying guide.

Examples

Working examples are in packages/tinfoil/examples/:

Example Description
chat/ Basic chat completion with TinfoilAI
streaming/ Server-sent events streaming
ai-sdk/ Vercel AI SDK server-side integration
ai-sdk-react/ Vercel AI SDK React/browser integration
secure_client/ Direct SecureClient usage for custom HTTP
unverified_client/ Development/testing without attestation (tinfoil/unsafe)

Run any example:

cd packages/tinfoil/examples/chat
npx ts-node main.ts

Documentation

Guides
Understanding the Security
Tutorials
Resources

Project Structure

This is a monorepo with two packages:

Package Description
packages/tinfoil Main SDK (published as tinfoil)
packages/verifier Attestation verifier (published as @tinfoilsh/verifier)

Development

# Install dependencies
npm install

# Build all packages
npm run build

# Run tests
npm test                    # Unit tests
npm run test:all            # All tests (unit + integration + browser)
npm run test:integration    # Integration tests (real network requests)
npm run test:browser        # Browser tests
npm run test:bun -w tinfoil # Bun tests

Reporting Vulnerabilities

Email security@tinfoil.sh or open a GitHub issue.

Keywords