@zeke-02/tinfoil 0.0.11 → 0.11.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-sdk-provider.browser.d.ts +12 -0
- package/dist/ai-sdk-provider.browser.d.ts.map +1 -0
- package/dist/{esm/ai-sdk-provider.mjs → ai-sdk-provider.browser.js} +9 -5
- package/dist/ai-sdk-provider.browser.js.map +1 -0
- package/dist/ai-sdk-provider.d.ts +6 -1
- package/dist/ai-sdk-provider.d.ts.map +1 -0
- package/dist/ai-sdk-provider.js +12 -11
- package/dist/ai-sdk-provider.js.map +1 -0
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +3 -5
- package/dist/config.js.map +1 -0
- package/dist/encrypted-body-fetch.d.ts +8 -2
- package/dist/encrypted-body-fetch.d.ts.map +1 -0
- package/dist/encrypted-body-fetch.js +27 -26
- package/dist/encrypted-body-fetch.js.map +1 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +2 -4
- package/dist/env.js.map +1 -0
- package/dist/index.browser.d.ts +8 -7
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.browser.js +8 -28
- package/dist/index.browser.js.map +1 -0
- package/dist/index.d.ts +9 -8
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -47
- package/dist/index.js.map +1 -0
- package/dist/pinned-tls-fetch.d.ts +1 -0
- package/dist/pinned-tls-fetch.d.ts.map +1 -0
- package/dist/pinned-tls-fetch.js +13 -17
- package/dist/pinned-tls-fetch.js.map +1 -0
- package/dist/router.d.ts +1 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +6 -7
- package/dist/router.js.map +1 -0
- package/dist/{esm/secure-client.d.ts → secure-client.browser.d.ts} +2 -1
- package/dist/secure-client.browser.d.ts.map +1 -0
- package/dist/{esm/secure-client.mjs → secure-client.browser.js} +46 -56
- package/dist/secure-client.browser.js.map +1 -0
- package/dist/secure-client.d.ts +2 -1
- package/dist/secure-client.d.ts.map +1 -0
- package/dist/secure-client.js +51 -65
- package/dist/secure-client.js.map +1 -0
- package/dist/secure-fetch.browser.d.ts +1 -0
- package/dist/secure-fetch.browser.d.ts.map +1 -0
- package/dist/secure-fetch.browser.js +4 -6
- package/dist/secure-fetch.browser.js.map +1 -0
- package/dist/secure-fetch.d.ts +1 -0
- package/dist/secure-fetch.d.ts.map +1 -0
- package/dist/secure-fetch.js +16 -8
- package/dist/secure-fetch.js.map +1 -0
- package/dist/{tinfoilai.d.ts → tinfoil-ai.browser.d.ts} +5 -2
- package/dist/tinfoil-ai.browser.d.ts.map +1 -0
- package/dist/{tinfoilai.js → tinfoil-ai.browser.js} +50 -39
- package/dist/tinfoil-ai.browser.js.map +1 -0
- package/dist/{esm/tinfoilai.d.ts → tinfoil-ai.d.ts} +5 -2
- package/dist/tinfoil-ai.d.ts.map +1 -0
- package/dist/{esm/tinfoilai.mjs → tinfoil-ai.js} +29 -11
- package/dist/tinfoil-ai.js.map +1 -0
- package/dist/unverified-client.d.ts +1 -2
- package/dist/unverified-client.d.ts.map +1 -0
- package/dist/unverified-client.js +10 -13
- package/dist/unverified-client.js.map +1 -0
- package/dist/verifier.d.ts +2 -141
- package/dist/verifier.d.ts.map +1 -0
- package/dist/verifier.js +2 -570
- package/dist/verifier.js.map +1 -0
- package/package.json +48 -41
- package/LICENSE +0 -661
- package/README.md +0 -183
- package/dist/__tests__/test-utils.d.ts +0 -1
- package/dist/__tests__/test-utils.js +0 -44
- package/dist/esm/__tests__/test-utils.d.ts +0 -1
- package/dist/esm/__tests__/test-utils.mjs +0 -38
- package/dist/esm/ai-sdk-provider.d.ts +0 -7
- package/dist/esm/config.d.ts +0 -13
- package/dist/esm/config.mjs +0 -13
- package/dist/esm/encrypted-body-fetch.d.ts +0 -13
- package/dist/esm/encrypted-body-fetch.mjs +0 -105
- package/dist/esm/env.d.ts +0 -5
- package/dist/esm/env.mjs +0 -17
- package/dist/esm/fetch-adapter.d.ts +0 -21
- package/dist/esm/fetch-adapter.mjs +0 -23
- package/dist/esm/index.browser.d.ts +0 -7
- package/dist/esm/index.browser.mjs +0 -8
- package/dist/esm/index.d.ts +0 -9
- package/dist/esm/index.mjs +0 -13
- package/dist/esm/pinned-tls-fetch.d.ts +0 -1
- package/dist/esm/pinned-tls-fetch.mjs +0 -110
- package/dist/esm/router.d.ts +0 -11
- package/dist/esm/router.mjs +0 -33
- package/dist/esm/secure-fetch.browser.d.ts +0 -1
- package/dist/esm/secure-fetch.browser.mjs +0 -10
- package/dist/esm/secure-fetch.d.ts +0 -1
- package/dist/esm/secure-fetch.mjs +0 -12
- package/dist/esm/unverified-client.d.ts +0 -18
- package/dist/esm/unverified-client.mjs +0 -61
- package/dist/esm/verifier.d.ts +0 -141
- package/dist/esm/verifier.mjs +0 -532
- package/dist/esm/wasm-exec.js +0 -668
- package/dist/esm/wasm-exec.mjs +0 -668
- package/dist/fetch-adapter.d.ts +0 -21
- package/dist/fetch-adapter.js +0 -27
- package/dist/wasm-exec.js +0 -668
package/README.md
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
# Tinfoil Node Client
|
|
2
|
-
|
|
3
|
-
[](https://github.com/tinfoilsh/tinfoil-node/actions)
|
|
4
|
-
[](https://npmjs.org/package/tinfoil)
|
|
5
|
-
[](https://docs.tinfoil.sh/sdk/node-sdk)
|
|
6
|
-
|
|
7
|
-
This client library provides secure and convenient access to the Tinfoil Private Inference endpoints from TypeScript or JavaScript.
|
|
8
|
-
|
|
9
|
-
It is a wrapper around the OpenAI client that verifies enclave attestation and routes traffic to the Tinfoil Private Inference endpoints through an [EHBP](https://github.com/tinfoilsh/encrypted-http-body-protocol)-secured transport. EHBP encrypts all payloads directly to an attested enclave using [HPKE (RFC 9180)](https://www.rfc-editor.org/rfc/rfc9180.html).
|
|
10
|
-
|
|
11
|
-
## Installation
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm install tinfoil
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Requirements
|
|
18
|
-
|
|
19
|
-
Node 20+.
|
|
20
|
-
|
|
21
|
-
## Quick Start
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
import { TinfoilAI } from "tinfoil";
|
|
25
|
-
|
|
26
|
-
const client = new TinfoilAI({
|
|
27
|
-
apiKey: "<YOUR_API_KEY>", // or use TINFOIL_API_KEY env var
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
// Uses identical method calls as the OpenAI client
|
|
31
|
-
const completion = await client.chat.completions.create({
|
|
32
|
-
messages: [{ role: "user", content: "Hello!" }],
|
|
33
|
-
model: "llama3-3-70b",
|
|
34
|
-
});
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Browser Support
|
|
38
|
-
|
|
39
|
-
The SDK supports browser environments. This allows you to use the secure enclave-backed OpenAI API directly from web applications.
|
|
40
|
-
|
|
41
|
-
### ⚠️ Security Warning
|
|
42
|
-
|
|
43
|
-
Using API keys directly in the browser exposes them to anyone who can view your page source.
|
|
44
|
-
For production applications, always use a backend server to handle API keys.
|
|
45
|
-
|
|
46
|
-
### Browser Usage
|
|
47
|
-
|
|
48
|
-
```javascript
|
|
49
|
-
import { TinfoilAI } from 'tinfoil';
|
|
50
|
-
|
|
51
|
-
const client = new TinfoilAI({
|
|
52
|
-
apiKey: 'your-api-key',
|
|
53
|
-
dangerouslyAllowBrowser: true // Required for browser usage
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// Optional: pre-initialize; you can also call APIs directly
|
|
57
|
-
await client.ready();
|
|
58
|
-
|
|
59
|
-
const completion = await client.chat.completions.create({
|
|
60
|
-
model: 'llama3-3-70b',
|
|
61
|
-
messages: [{ role: 'user', content: 'Hello!' }]
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Browser Requirements
|
|
66
|
-
|
|
67
|
-
- Modern browsers with ES2020 support
|
|
68
|
-
- WebAssembly support for enclave verification
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
## Verification helpers
|
|
72
|
-
|
|
73
|
-
This package exposes verification helpers that load the Go-based WebAssembly verifier and provide end-to-end attestation with structured, stepwise results you can use in applications (e.g., to show progress, log transitions, or gate features).
|
|
74
|
-
|
|
75
|
-
The verification functionality is contained in `verifier.ts`.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
### Core Verifier API
|
|
79
|
-
|
|
80
|
-
```typescript
|
|
81
|
-
import { Verifier } from "tinfoil";
|
|
82
|
-
|
|
83
|
-
const verifier = new Verifier({ serverURL: "https://enclave.host.com" });
|
|
84
|
-
|
|
85
|
-
// Perform full end-to-end verification
|
|
86
|
-
const attestation = await verifier.verify();
|
|
87
|
-
// Returns: AttestationResponse with measurement and cryptographic keys
|
|
88
|
-
// This performs all verification steps atomically:
|
|
89
|
-
// 1. Fetches the latest release digest from GitHub
|
|
90
|
-
// 2. Verifies code provenance using Sigstore
|
|
91
|
-
// 3. Performs runtime attestation against the enclave
|
|
92
|
-
// 4. Verifies hardware measurements (for TDX platforms)
|
|
93
|
-
// 5. Compares code and runtime measurements
|
|
94
|
-
|
|
95
|
-
// Access detailed verification results
|
|
96
|
-
const doc = verifier.getVerificationDocument();
|
|
97
|
-
// Returns: VerificationDocument with step-by-step status including
|
|
98
|
-
// measurements, fingerprints, and cryptographic keys
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Verification Document
|
|
102
|
-
|
|
103
|
-
The `Verifier` provides access to a comprehensive verification document that tracks all verification steps, including failures:
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
import { Verifier } from "tinfoil";
|
|
107
|
-
|
|
108
|
-
const verifier = new Verifier({ serverURL: "https://enclave.host.com" });
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
const attestation = await verifier.verify();
|
|
112
|
-
const doc = verifier.getVerificationDocument();
|
|
113
|
-
console.log('Security verified:', doc.securityVerified);
|
|
114
|
-
console.log('TLS fingerprint:', attestation.tlsPublicKeyFingerprint);
|
|
115
|
-
console.log('HPKE public key:', attestation.hpkePublicKey);
|
|
116
|
-
} catch (error) {
|
|
117
|
-
// Even on error, you can access the verification document
|
|
118
|
-
const doc = verifier.getVerificationDocument();
|
|
119
|
-
|
|
120
|
-
// The document contains detailed step information:
|
|
121
|
-
// - fetchDigest: GitHub release digest retrieval
|
|
122
|
-
// - verifyCode: Code measurement verification
|
|
123
|
-
// - verifyEnclave: Runtime attestation verification
|
|
124
|
-
// - compareMeasurements: Code vs runtime measurement comparison
|
|
125
|
-
// - otherError: Catch-all for unexpected errors (optional)
|
|
126
|
-
|
|
127
|
-
// Check individual steps
|
|
128
|
-
if (doc.steps.verifyEnclave.status === 'failed') {
|
|
129
|
-
console.log('Enclave verification failed:', doc.steps.verifyEnclave.error);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Error messages are prefixed with the failing step:
|
|
133
|
-
// - "fetchDigest:" - Failed to fetch GitHub release digest
|
|
134
|
-
// - "verifyCode:" - Failed to verify code provenance
|
|
135
|
-
// - "verifyEnclave:" - Failed runtime attestation
|
|
136
|
-
// - "verifyHardware:" - Failed TDX hardware verification
|
|
137
|
-
// - "validateTLS:" - TLS public key validation failed
|
|
138
|
-
// - "measurements:" - Measurement comparison failed
|
|
139
|
-
}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## Testing
|
|
143
|
-
|
|
144
|
-
The project includes both unit tests and integration tests:
|
|
145
|
-
|
|
146
|
-
### Running Unit Tests
|
|
147
|
-
|
|
148
|
-
```bash
|
|
149
|
-
npm test
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Running Integration Tests
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
RUN_TINFOIL_INTEGRATION=true npm test
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
This runs the full test suite including integration tests that:
|
|
159
|
-
- Make actual network requests to Tinfoil services
|
|
160
|
-
- Perform real enclave attestation verification
|
|
161
|
-
- Test end-to-end functionality with live services
|
|
162
|
-
|
|
163
|
-
Integration tests are skipped by default to keep the test suite fast and avoid network dependencies during development.
|
|
164
|
-
|
|
165
|
-
## Running examples
|
|
166
|
-
|
|
167
|
-
See [examples/README.md](https://github.com/tinfoilsh/tinfoil-node/blob/main/examples/README.md).
|
|
168
|
-
|
|
169
|
-
## API Documentation
|
|
170
|
-
|
|
171
|
-
For complete documentation on using the Tinfoil Node SDK, including advanced examples and API reference, visit the [official documentation](https://docs.tinfoil.sh/sdk/node-sdk).
|
|
172
|
-
|
|
173
|
-
This library mirrors the official OpenAI Node.js client for common endpoints (e.g., chat, images, embeddings) and types, and is designed to feel familiar. Some less commonly used surfaces may not be fully covered. See the [OpenAI client](https://github.com/openai/openai-node) for complete API usage and documentation.
|
|
174
|
-
|
|
175
|
-
## Reporting Vulnerabilities
|
|
176
|
-
|
|
177
|
-
Please report security vulnerabilities by either:
|
|
178
|
-
|
|
179
|
-
- Emailing [security@tinfoil.sh](mailto:security@tinfoil.sh)
|
|
180
|
-
|
|
181
|
-
- Opening an issue on GitHub on this repository
|
|
182
|
-
|
|
183
|
-
We aim to respond to security reports within 24 hours and will keep you updated on our progress.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function withMockedModules(mocks: Record<string, unknown>, modulesToReload: string[], run: () => Promise<void>): Promise<void>;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.withMockedModules = withMockedModules;
|
|
7
|
-
const module_1 = __importDefault(require("module"));
|
|
8
|
-
const testRequire = module_1.default.createRequire(__filename);
|
|
9
|
-
async function withMockedModules(mocks, modulesToReload, run) {
|
|
10
|
-
const moduleAny = module_1.default;
|
|
11
|
-
const originalLoad = moduleAny._load;
|
|
12
|
-
moduleAny._load = function (request, parent, isMain) {
|
|
13
|
-
if (Object.prototype.hasOwnProperty.call(mocks, request)) {
|
|
14
|
-
return mocks[request];
|
|
15
|
-
}
|
|
16
|
-
// eslint-disable-next-line prefer-rest-params
|
|
17
|
-
return originalLoad.apply(this, arguments);
|
|
18
|
-
};
|
|
19
|
-
const restoredCache = [];
|
|
20
|
-
for (const specifier of modulesToReload) {
|
|
21
|
-
try {
|
|
22
|
-
const resolved = testRequire.resolve(specifier);
|
|
23
|
-
restoredCache.push({ path: resolved, cached: require.cache[resolved] });
|
|
24
|
-
delete require.cache[resolved];
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
// Module not yet cached; nothing to remove.
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
try {
|
|
31
|
-
await run();
|
|
32
|
-
}
|
|
33
|
-
finally {
|
|
34
|
-
moduleAny._load = originalLoad;
|
|
35
|
-
for (const entry of restoredCache) {
|
|
36
|
-
if (entry.cached) {
|
|
37
|
-
require.cache[entry.path] = entry.cached;
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
delete require.cache[entry.path];
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function withMockedModules(mocks: Record<string, unknown>, modulesToReload: string[], run: () => Promise<void>): Promise<void>;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import Module from "module";
|
|
2
|
-
const testRequire = Module.createRequire(__filename);
|
|
3
|
-
export async function withMockedModules(mocks, modulesToReload, run) {
|
|
4
|
-
const moduleAny = Module;
|
|
5
|
-
const originalLoad = moduleAny._load;
|
|
6
|
-
moduleAny._load = function (request, parent, isMain) {
|
|
7
|
-
if (Object.prototype.hasOwnProperty.call(mocks, request)) {
|
|
8
|
-
return mocks[request];
|
|
9
|
-
}
|
|
10
|
-
// eslint-disable-next-line prefer-rest-params
|
|
11
|
-
return originalLoad.apply(this, arguments);
|
|
12
|
-
};
|
|
13
|
-
const restoredCache = [];
|
|
14
|
-
for (const specifier of modulesToReload) {
|
|
15
|
-
try {
|
|
16
|
-
const resolved = testRequire.resolve(specifier);
|
|
17
|
-
restoredCache.push({ path: resolved, cached: require.cache[resolved] });
|
|
18
|
-
delete require.cache[resolved];
|
|
19
|
-
}
|
|
20
|
-
catch {
|
|
21
|
-
// Module not yet cached; nothing to remove.
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
try {
|
|
25
|
-
await run();
|
|
26
|
-
}
|
|
27
|
-
finally {
|
|
28
|
-
moduleAny._load = originalLoad;
|
|
29
|
-
for (const entry of restoredCache) {
|
|
30
|
-
if (entry.cached) {
|
|
31
|
-
require.cache[entry.path] = entry.cached;
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
delete require.cache[entry.path];
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
interface CreateTinfoilAIOptions {
|
|
2
|
-
baseURL?: string;
|
|
3
|
-
enclaveURL?: string;
|
|
4
|
-
configRepo?: string;
|
|
5
|
-
}
|
|
6
|
-
export declare function createTinfoilAI(apiKey: string, options?: CreateTinfoilAIOptions, headers?: Record<string, string>): Promise<import("@ai-sdk/openai-compatible").OpenAICompatibleProvider<string, string, string, string>>;
|
|
7
|
-
export {};
|
package/dist/esm/config.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration constants for the Tinfoil Node SDK
|
|
3
|
-
*/
|
|
4
|
-
export declare const TINFOIL_CONFIG: {
|
|
5
|
-
/**
|
|
6
|
-
* The GitHub repository for code attestation verification
|
|
7
|
-
*/
|
|
8
|
-
readonly INFERENCE_PROXY_REPO: "tinfoilsh/confidential-model-router";
|
|
9
|
-
/**
|
|
10
|
-
* The ATC (Attestation and Trust Center) API URL for fetching available routers
|
|
11
|
-
*/
|
|
12
|
-
readonly ATC_API_URL: "https://atc.tinfoil.sh/routers";
|
|
13
|
-
};
|
package/dist/esm/config.mjs
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration constants for the Tinfoil Node SDK
|
|
3
|
-
*/
|
|
4
|
-
export const TINFOIL_CONFIG = {
|
|
5
|
-
/**
|
|
6
|
-
* The GitHub repository for code attestation verification
|
|
7
|
-
*/
|
|
8
|
-
INFERENCE_PROXY_REPO: "tinfoilsh/confidential-model-router",
|
|
9
|
-
/**
|
|
10
|
-
* The ATC (Attestation and Trust Center) API URL for fetching available routers
|
|
11
|
-
*/
|
|
12
|
-
ATC_API_URL: "https://atc.tinfoil.sh/routers",
|
|
13
|
-
};
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Transport as EhbpTransport } from "@zeke-02/ehbp";
|
|
2
|
-
export declare function getHPKEKey(enclaveURL: string): Promise<CryptoKey>;
|
|
3
|
-
export declare function normalizeEncryptedBodyRequestArgs(input: RequestInfo | URL, init?: RequestInit): {
|
|
4
|
-
url: string;
|
|
5
|
-
init?: RequestInit;
|
|
6
|
-
};
|
|
7
|
-
export declare function encryptedBodyRequest(input: RequestInfo | URL, hpkePublicKey?: string, init?: RequestInit, enclaveURL?: string, transportInstance?: EhbpTransport): Promise<Response>;
|
|
8
|
-
type FetchWithResponse = typeof fetch & {
|
|
9
|
-
Response: typeof Response;
|
|
10
|
-
};
|
|
11
|
-
export declare function createEncryptedBodyFetch(baseURL: string, hpkePublicKey?: string, enclaveURL?: string): FetchWithResponse;
|
|
12
|
-
export declare function getTransportForOrigin(origin: string, keyOrigin: string): Promise<EhbpTransport>;
|
|
13
|
-
export {};
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { Identity, Transport, PROTOCOL } from "@zeke-02/ehbp";
|
|
2
|
-
import { getFetch } from "./fetch-adapter.mjs";
|
|
3
|
-
// Public API
|
|
4
|
-
export async function getHPKEKey(enclaveURL) {
|
|
5
|
-
const url = new URL(enclaveURL);
|
|
6
|
-
const keysURL = new URL(PROTOCOL.KEYS_PATH, enclaveURL);
|
|
7
|
-
if (keysURL.protocol !== "https:") {
|
|
8
|
-
throw new Error(`HTTPS is required for remote key retrieval. Invalid protocol: ${keysURL.protocol}`);
|
|
9
|
-
}
|
|
10
|
-
const fetchFn = getFetch();
|
|
11
|
-
const response = await fetchFn(keysURL.toString());
|
|
12
|
-
if (!response.ok) {
|
|
13
|
-
throw new Error(`Failed to get server public key: ${response.status}`);
|
|
14
|
-
}
|
|
15
|
-
const contentType = response.headers.get("content-type");
|
|
16
|
-
if (contentType !== PROTOCOL.KEYS_MEDIA_TYPE) {
|
|
17
|
-
throw new Error(`Invalid content type: ${contentType}`);
|
|
18
|
-
}
|
|
19
|
-
const keysData = new Uint8Array(await response.arrayBuffer());
|
|
20
|
-
const serverIdentity = await Identity.unmarshalPublicConfig(keysData);
|
|
21
|
-
return serverIdentity.getPublicKey();
|
|
22
|
-
}
|
|
23
|
-
export function normalizeEncryptedBodyRequestArgs(input, init) {
|
|
24
|
-
if (typeof input === "string") {
|
|
25
|
-
return { url: input, init };
|
|
26
|
-
}
|
|
27
|
-
if (input instanceof URL) {
|
|
28
|
-
return { url: input.toString(), init };
|
|
29
|
-
}
|
|
30
|
-
const request = input;
|
|
31
|
-
const cloned = request.clone();
|
|
32
|
-
const derivedInit = {
|
|
33
|
-
method: cloned.method,
|
|
34
|
-
headers: new Headers(cloned.headers),
|
|
35
|
-
body: cloned.body ?? undefined,
|
|
36
|
-
signal: cloned.signal,
|
|
37
|
-
};
|
|
38
|
-
return {
|
|
39
|
-
url: cloned.url,
|
|
40
|
-
init: { ...derivedInit, ...init },
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
export async function encryptedBodyRequest(input, hpkePublicKey, init, enclaveURL, transportInstance) {
|
|
44
|
-
const { url: requestUrl, init: requestInit } = normalizeEncryptedBodyRequestArgs(input, init);
|
|
45
|
-
let actualTransport;
|
|
46
|
-
if (transportInstance) {
|
|
47
|
-
// Use provided transport instance
|
|
48
|
-
actualTransport = transportInstance;
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
// Create a new transport for this request
|
|
52
|
-
const u = new URL(requestUrl);
|
|
53
|
-
const { origin } = u;
|
|
54
|
-
const keyOrigin = enclaveURL ? new URL(enclaveURL).origin : origin;
|
|
55
|
-
actualTransport = await getTransportForOrigin(origin, keyOrigin);
|
|
56
|
-
}
|
|
57
|
-
if (hpkePublicKey) {
|
|
58
|
-
const transportKeyHash = await actualTransport.getServerPublicKeyHex();
|
|
59
|
-
if (transportKeyHash !== hpkePublicKey) {
|
|
60
|
-
throw new Error(`HPKE public key mismatch. Expected: ${hpkePublicKey}, Got: ${transportKeyHash}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return actualTransport.request(requestUrl, requestInit);
|
|
64
|
-
}
|
|
65
|
-
export function createEncryptedBodyFetch(baseURL, hpkePublicKey, enclaveURL) {
|
|
66
|
-
// Create a dedicated transport instance for this fetch function
|
|
67
|
-
let transportPromise = null;
|
|
68
|
-
const getOrCreateTransport = async () => {
|
|
69
|
-
if (!transportPromise) {
|
|
70
|
-
const baseUrl = new URL(baseURL);
|
|
71
|
-
const keyOrigin = enclaveURL
|
|
72
|
-
? new URL(enclaveURL).origin
|
|
73
|
-
: baseUrl.origin;
|
|
74
|
-
transportPromise = getTransportForOrigin(baseUrl.origin, keyOrigin);
|
|
75
|
-
}
|
|
76
|
-
return transportPromise;
|
|
77
|
-
};
|
|
78
|
-
const secureFetch = (async (input, init) => {
|
|
79
|
-
const normalized = normalizeEncryptedBodyRequestArgs(input, init);
|
|
80
|
-
const targetUrl = new URL(normalized.url, baseURL);
|
|
81
|
-
// Get the dedicated transport instance for this fetch function
|
|
82
|
-
const transportInstance = await getOrCreateTransport();
|
|
83
|
-
return encryptedBodyRequest(targetUrl.toString(), hpkePublicKey, normalized.init, enclaveURL, transportInstance);
|
|
84
|
-
});
|
|
85
|
-
// Expose Response constructor for OpenAI SDK's FormData support detection
|
|
86
|
-
// This prevents the SDK from making a test request to 'data:,' which would fail through EHBP
|
|
87
|
-
secureFetch.Response = Response;
|
|
88
|
-
return secureFetch;
|
|
89
|
-
}
|
|
90
|
-
export async function getTransportForOrigin(origin, keyOrigin) {
|
|
91
|
-
if (typeof globalThis !== "undefined") {
|
|
92
|
-
const isSecure = globalThis.isSecureContext !== false;
|
|
93
|
-
const hasSubtle = !!(globalThis.crypto && globalThis.crypto.subtle);
|
|
94
|
-
if (!isSecure || !hasSubtle) {
|
|
95
|
-
const reason = !isSecure
|
|
96
|
-
? "insecure context (use HTTPS or localhost)"
|
|
97
|
-
: "missing WebCrypto SubtleCrypto";
|
|
98
|
-
throw new Error(`EHBP requires a secure browser context: ${reason}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
const clientIdentity = await Identity.generate();
|
|
102
|
-
const serverPublicKey = await getHPKEKey(keyOrigin);
|
|
103
|
-
const requestHost = new URL(origin).host;
|
|
104
|
-
return new Transport(clientIdentity, requestHost, serverPublicKey);
|
|
105
|
-
}
|
package/dist/esm/env.d.ts
DELETED
package/dist/esm/env.mjs
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detects if the code is running in a real browser environment.
|
|
3
|
-
* Returns false for Node.js environments, even with WASM loaded.
|
|
4
|
-
*/
|
|
5
|
-
export function isRealBrowser() {
|
|
6
|
-
if (typeof process !== "undefined" &&
|
|
7
|
-
process.versions &&
|
|
8
|
-
process.versions.node) {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
if (typeof window !== "undefined" && typeof window.document !== "undefined") {
|
|
12
|
-
if (typeof navigator !== "undefined" && navigator.userAgent) {
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fetch adapter for Tauri v2
|
|
3
|
-
*
|
|
4
|
-
* This module provides a centralized fetch implementation for Tauri v2.
|
|
5
|
-
* For testing purposes, the fetch function can be overridden by setting
|
|
6
|
-
* the global __TINFOIL_TEST_FETCH__ property.
|
|
7
|
-
*/
|
|
8
|
-
import { fetch as tauriFetch } from "@tauri-apps/plugin-http";
|
|
9
|
-
declare global {
|
|
10
|
-
var __TINFOIL_TEST_FETCH__: typeof fetch | undefined;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Get the fetch implementation to use.
|
|
14
|
-
* In tests, this can be overridden by setting globalThis.__TINFOIL_TEST_FETCH__
|
|
15
|
-
*/
|
|
16
|
-
export declare function getFetch(): typeof tauriFetch;
|
|
17
|
-
/**
|
|
18
|
-
* The fetch function to use throughout the application.
|
|
19
|
-
* Uses Tauri's fetch by default, but can be mocked for testing.
|
|
20
|
-
*/
|
|
21
|
-
export declare const fetch: typeof tauriFetch;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fetch adapter for Tauri v2
|
|
3
|
-
*
|
|
4
|
-
* This module provides a centralized fetch implementation for Tauri v2.
|
|
5
|
-
* For testing purposes, the fetch function can be overridden by setting
|
|
6
|
-
* the global __TINFOIL_TEST_FETCH__ property.
|
|
7
|
-
*/
|
|
8
|
-
import { fetch as tauriFetch } from "@tauri-apps/plugin-http";
|
|
9
|
-
/**
|
|
10
|
-
* Get the fetch implementation to use.
|
|
11
|
-
* In tests, this can be overridden by setting globalThis.__TINFOIL_TEST_FETCH__
|
|
12
|
-
*/
|
|
13
|
-
export function getFetch() {
|
|
14
|
-
if (typeof globalThis.__TINFOIL_TEST_FETCH__ === "function") {
|
|
15
|
-
return globalThis.__TINFOIL_TEST_FETCH__;
|
|
16
|
-
}
|
|
17
|
-
return tauriFetch;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* The fetch function to use throughout the application.
|
|
21
|
-
* Uses Tauri's fetch by default, but can be mocked for testing.
|
|
22
|
-
*/
|
|
23
|
-
export const fetch = getFetch();
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { TinfoilAI } from "./tinfoilai";
|
|
2
|
-
export { TinfoilAI as default } from "./tinfoilai";
|
|
3
|
-
export * from "./verifier";
|
|
4
|
-
export * from "./ai-sdk-provider";
|
|
5
|
-
export * from "./config";
|
|
6
|
-
export { SecureClient } from "./secure-client";
|
|
7
|
-
export { UnverifiedClient } from "./unverified-client";
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// Browser-safe entry point: avoids Node built-ins
|
|
2
|
-
export { TinfoilAI } from "./tinfoilai.mjs";
|
|
3
|
-
export { TinfoilAI as default } from "./tinfoilai.mjs";
|
|
4
|
-
export * from "./verifier.mjs";
|
|
5
|
-
export * from "./ai-sdk-provider.mjs";
|
|
6
|
-
export * from "./config.mjs";
|
|
7
|
-
export { SecureClient } from "./secure-client.mjs";
|
|
8
|
-
export { UnverifiedClient } from "./unverified-client.mjs";
|
package/dist/esm/index.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export { TinfoilAI } from "./tinfoilai";
|
|
2
|
-
export { TinfoilAI as default } from "./tinfoilai";
|
|
3
|
-
export * from "./verifier";
|
|
4
|
-
export * from "./ai-sdk-provider";
|
|
5
|
-
export * from "./config";
|
|
6
|
-
export { SecureClient } from "./secure-client";
|
|
7
|
-
export { UnverifiedClient } from "./unverified-client";
|
|
8
|
-
export { fetchRouter } from "./router";
|
|
9
|
-
export { type Uploadable, toFile, APIPromise, PagePromise, OpenAIError, APIError, APIConnectionError, APIConnectionTimeoutError, APIUserAbortError, NotFoundError, ConflictError, RateLimitError, BadRequestError, AuthenticationError, InternalServerError, PermissionDeniedError, UnprocessableEntityError, } from "openai";
|
package/dist/esm/index.mjs
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
// Re-export the TinfoilAI class
|
|
2
|
-
export { TinfoilAI } from "./tinfoilai.mjs";
|
|
3
|
-
export { TinfoilAI as default } from "./tinfoilai.mjs";
|
|
4
|
-
// Export verifier
|
|
5
|
-
export * from "./verifier.mjs";
|
|
6
|
-
export * from "./ai-sdk-provider.mjs";
|
|
7
|
-
export * from "./config.mjs";
|
|
8
|
-
export { SecureClient } from "./secure-client.mjs";
|
|
9
|
-
export { UnverifiedClient } from "./unverified-client.mjs";
|
|
10
|
-
export { fetchRouter } from "./router.mjs";
|
|
11
|
-
// Re-export OpenAI utility types and classes that users might need
|
|
12
|
-
// Using public exports from the main OpenAI package instead of deep imports
|
|
13
|
-
export { toFile, APIPromise, PagePromise, OpenAIError, APIError, APIConnectionError, APIConnectionTimeoutError, APIUserAbortError, NotFoundError, ConflictError, RateLimitError, BadRequestError, AuthenticationError, InternalServerError, PermissionDeniedError, UnprocessableEntityError, } from "openai";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function createPinnedTlsFetch(baseURL: string, expectedFingerprintHex: string): typeof fetch;
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import https from "https";
|
|
2
|
-
import { checkServerIdentity as tlsCheckServerIdentity } from "tls";
|
|
3
|
-
import { X509Certificate, createHash } from "crypto";
|
|
4
|
-
import { Readable } from "stream";
|
|
5
|
-
export function createPinnedTlsFetch(baseURL, expectedFingerprintHex) {
|
|
6
|
-
return (async (input, init) => {
|
|
7
|
-
// Normalize URL with base URL support
|
|
8
|
-
const makeURL = (value) => {
|
|
9
|
-
if (typeof value === "string")
|
|
10
|
-
return new URL(value, baseURL);
|
|
11
|
-
if (value instanceof URL)
|
|
12
|
-
return value;
|
|
13
|
-
return new URL(value.url, baseURL);
|
|
14
|
-
};
|
|
15
|
-
const url = makeURL(input);
|
|
16
|
-
if (url.protocol !== "https:") {
|
|
17
|
-
throw new Error(`HTTP connections are not allowed. Use HTTPS. URL: ${url.toString()}`);
|
|
18
|
-
}
|
|
19
|
-
// Gather method and headers
|
|
20
|
-
const method = (init?.method || input.method || "GET").toUpperCase();
|
|
21
|
-
const headers = new Headers(init?.headers || input?.headers || {});
|
|
22
|
-
const headerObj = {};
|
|
23
|
-
headers.forEach((v, k) => {
|
|
24
|
-
headerObj[k] = v;
|
|
25
|
-
});
|
|
26
|
-
// Resolve body
|
|
27
|
-
let body = init?.body;
|
|
28
|
-
if (!body && input instanceof Request) {
|
|
29
|
-
// If the original was a Request with a body, read it
|
|
30
|
-
try {
|
|
31
|
-
const buf = await input.arrayBuffer();
|
|
32
|
-
if (buf && buf.byteLength)
|
|
33
|
-
body = Buffer.from(buf);
|
|
34
|
-
}
|
|
35
|
-
catch { }
|
|
36
|
-
}
|
|
37
|
-
// Convert web streams to Node streams if needed
|
|
38
|
-
if (body && typeof body.getReader === "function") {
|
|
39
|
-
body = Readable.fromWeb(body);
|
|
40
|
-
}
|
|
41
|
-
if (body instanceof ArrayBuffer) {
|
|
42
|
-
body = Buffer.from(body);
|
|
43
|
-
}
|
|
44
|
-
if (ArrayBuffer.isView(body)) {
|
|
45
|
-
body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
|
|
46
|
-
}
|
|
47
|
-
const requestOptions = {
|
|
48
|
-
protocol: url.protocol,
|
|
49
|
-
hostname: url.hostname,
|
|
50
|
-
port: url.port ? Number(url.port) : 443,
|
|
51
|
-
path: `${url.pathname}${url.search}`,
|
|
52
|
-
method,
|
|
53
|
-
headers: headerObj,
|
|
54
|
-
checkServerIdentity: (host, cert) => {
|
|
55
|
-
const raw = cert.raw;
|
|
56
|
-
if (!raw) {
|
|
57
|
-
return new Error("Certificate raw bytes are unavailable for pinning");
|
|
58
|
-
}
|
|
59
|
-
const x509 = new X509Certificate(raw);
|
|
60
|
-
const publicKeyDer = x509.publicKey.export({ type: "spki", format: "der" });
|
|
61
|
-
const fp = createHash("sha256").update(publicKeyDer).digest("hex");
|
|
62
|
-
if (fp !== expectedFingerprintHex) {
|
|
63
|
-
return new Error(`Certificate public key fingerprint mismatch. Expected: ${expectedFingerprintHex}, Got: ${fp}`);
|
|
64
|
-
}
|
|
65
|
-
return tlsCheckServerIdentity(host, cert);
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
const { signal } = init || {};
|
|
69
|
-
const res = await new Promise((resolve, reject) => {
|
|
70
|
-
const req = https.request(requestOptions, resolve);
|
|
71
|
-
req.on("error", reject);
|
|
72
|
-
if (signal) {
|
|
73
|
-
if (signal.aborted) {
|
|
74
|
-
req.destroy(new Error("Request aborted"));
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
signal.addEventListener("abort", () => req.destroy(new Error("Request aborted")));
|
|
78
|
-
}
|
|
79
|
-
if (body === undefined || body === null) {
|
|
80
|
-
req.end();
|
|
81
|
-
}
|
|
82
|
-
else if (typeof body === "string" || Buffer.isBuffer(body) || ArrayBuffer.isView(body)) {
|
|
83
|
-
req.end(body);
|
|
84
|
-
}
|
|
85
|
-
else if (typeof body.pipe === "function") {
|
|
86
|
-
body.pipe(req);
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
// Fallback: try to serialize objects
|
|
90
|
-
req.end(String(body));
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
const responseHeaders = new Headers();
|
|
94
|
-
for (const [k, v] of Object.entries(res.headers)) {
|
|
95
|
-
if (Array.isArray(v)) {
|
|
96
|
-
v.forEach(item => responseHeaders.append(k, item));
|
|
97
|
-
}
|
|
98
|
-
else if (v != null) {
|
|
99
|
-
responseHeaders.set(k, String(v));
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
// Convert Node stream to Web ReadableStream
|
|
103
|
-
const webStream = Readable.toWeb(res);
|
|
104
|
-
return new Response(webStream, {
|
|
105
|
-
status: res.statusCode || 0,
|
|
106
|
-
statusText: res.statusMessage || "",
|
|
107
|
-
headers: responseHeaders,
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
}
|
package/dist/esm/router.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Router utilities for fetching available Tinfoil routers
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Fetches the list of available routers from the ATC API
|
|
6
|
-
* and returns a randomly selected address.
|
|
7
|
-
*
|
|
8
|
-
* @returns Promise<string> A randomly selected router address
|
|
9
|
-
* @throws Error if no routers are found or if the request fails
|
|
10
|
-
*/
|
|
11
|
-
export declare function fetchRouter(): Promise<string>;
|