@zeke-02/tinfoil 0.0.4 → 0.0.6
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/README.md +38 -24
- package/dist/ai-sdk-provider.d.ts +1 -1
- package/dist/ai-sdk-provider.js +2 -1
- package/dist/esm/ai-sdk-provider.d.ts +1 -1
- package/dist/esm/ai-sdk-provider.mjs +2 -1
- package/dist/esm/secure-client.mjs +8 -0
- package/dist/esm/secure-fetch.mjs +2 -12
- package/dist/esm/verifier.d.ts +49 -49
- package/dist/esm/verifier.mjs +150 -362
- package/dist/secure-client.js +8 -0
- package/dist/secure-fetch.js +2 -12
- package/dist/verifier.d.ts +49 -49
- package/dist/verifier.js +150 -364
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/tinfoilsh/tinfoil-node/actions)
|
|
4
4
|
[](https://npmjs.org/package/tinfoil)
|
|
5
|
+
[](https://docs.tinfoil.sh/sdk/node-sdk)
|
|
5
6
|
|
|
6
|
-
This client library provides secure and convenient access to the Tinfoil
|
|
7
|
+
This client library provides secure and convenient access to the Tinfoil Private Inference endpoints from TypeScript or JavaScript.
|
|
7
8
|
|
|
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).
|
|
9
10
|
|
|
@@ -69,7 +70,7 @@ const completion = await client.chat.completions.create({
|
|
|
69
70
|
|
|
70
71
|
## Verification helpers
|
|
71
72
|
|
|
72
|
-
This package exposes verification helpers that load the Go-based WebAssembly verifier
|
|
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).
|
|
73
74
|
|
|
74
75
|
The verification functionality is contained in `verifier.ts`.
|
|
75
76
|
|
|
@@ -79,51 +80,62 @@ The verification functionality is contained in `verifier.ts`.
|
|
|
79
80
|
```typescript
|
|
80
81
|
import { Verifier } from "tinfoil";
|
|
81
82
|
|
|
82
|
-
const verifier = new Verifier();
|
|
83
|
-
|
|
84
|
-
// Perform
|
|
85
|
-
const
|
|
86
|
-
// Returns:
|
|
87
|
-
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
//
|
|
93
|
-
|
|
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
|
|
94
99
|
```
|
|
95
100
|
|
|
96
101
|
### Verification Document
|
|
97
102
|
|
|
98
|
-
The `
|
|
103
|
+
The `Verifier` provides access to a comprehensive verification document that tracks all verification steps, including failures:
|
|
99
104
|
|
|
100
105
|
```typescript
|
|
101
|
-
import {
|
|
106
|
+
import { Verifier } from "tinfoil";
|
|
102
107
|
|
|
103
|
-
const
|
|
108
|
+
const verifier = new Verifier({ serverURL: "https://enclave.host.com" });
|
|
104
109
|
|
|
105
110
|
try {
|
|
106
|
-
await
|
|
107
|
-
const
|
|
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);
|
|
108
116
|
} catch (error) {
|
|
109
117
|
// Even on error, you can access the verification document
|
|
110
|
-
const doc =
|
|
118
|
+
const doc = verifier.getVerificationDocument();
|
|
111
119
|
|
|
112
120
|
// The document contains detailed step information:
|
|
113
121
|
// - fetchDigest: GitHub release digest retrieval
|
|
114
122
|
// - verifyCode: Code measurement verification
|
|
115
123
|
// - verifyEnclave: Runtime attestation verification
|
|
116
124
|
// - compareMeasurements: Code vs runtime measurement comparison
|
|
117
|
-
// - createTransport: Transport initialization (optional)
|
|
118
|
-
// - verifyHPKEKey: HPKE key verification (optional)
|
|
119
125
|
// - otherError: Catch-all for unexpected errors (optional)
|
|
120
126
|
|
|
121
|
-
console.log('Security verified:', doc.securityVerified);
|
|
122
|
-
|
|
123
127
|
// Check individual steps
|
|
124
128
|
if (doc.steps.verifyEnclave.status === 'failed') {
|
|
125
129
|
console.log('Enclave verification failed:', doc.steps.verifyEnclave.error);
|
|
126
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
|
|
127
139
|
}
|
|
128
140
|
```
|
|
129
141
|
|
|
@@ -156,6 +168,8 @@ See [examples/README.md](https://github.com/tinfoilsh/tinfoil-node/blob/main/exa
|
|
|
156
168
|
|
|
157
169
|
## API Documentation
|
|
158
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
|
+
|
|
159
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.
|
|
160
174
|
|
|
161
175
|
## Reporting Vulnerabilities
|
|
@@ -3,5 +3,5 @@ interface CreateTinfoilAIOptions {
|
|
|
3
3
|
enclaveURL?: string;
|
|
4
4
|
configRepo?: string;
|
|
5
5
|
}
|
|
6
|
-
export declare function createTinfoilAI(apiKey: string, options?: CreateTinfoilAIOptions): Promise<import("@ai-sdk/openai-compatible").OpenAICompatibleProvider<string, string, string, string>>;
|
|
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
7
|
export {};
|
package/dist/ai-sdk-provider.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.createTinfoilAI = createTinfoilAI;
|
|
|
4
4
|
const openai_compatible_1 = require("@ai-sdk/openai-compatible");
|
|
5
5
|
const config_1 = require("./config");
|
|
6
6
|
const secure_client_1 = require("./secure-client");
|
|
7
|
-
async function createTinfoilAI(apiKey, options = {}) {
|
|
7
|
+
async function createTinfoilAI(apiKey, options = {}, headers = {}) {
|
|
8
8
|
const baseURL = options.baseURL;
|
|
9
9
|
const enclaveURL = options.enclaveURL;
|
|
10
10
|
const configRepo = options.configRepo || config_1.TINFOIL_CONFIG.INFERENCE_PROXY_REPO;
|
|
@@ -24,5 +24,6 @@ async function createTinfoilAI(apiKey, options = {}) {
|
|
|
24
24
|
baseURL: finalBaseURL,
|
|
25
25
|
apiKey: apiKey,
|
|
26
26
|
fetch: secureClient.fetch,
|
|
27
|
+
headers,
|
|
27
28
|
});
|
|
28
29
|
}
|
|
@@ -3,5 +3,5 @@ interface CreateTinfoilAIOptions {
|
|
|
3
3
|
enclaveURL?: string;
|
|
4
4
|
configRepo?: string;
|
|
5
5
|
}
|
|
6
|
-
export declare function createTinfoilAI(apiKey: string, options?: CreateTinfoilAIOptions): Promise<import("@ai-sdk/openai-compatible").OpenAICompatibleProvider<string, string, string, string>>;
|
|
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
7
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
2
2
|
import { TINFOIL_CONFIG } from "./config.mjs";
|
|
3
3
|
import { SecureClient } from "./secure-client.mjs";
|
|
4
|
-
export async function createTinfoilAI(apiKey, options = {}) {
|
|
4
|
+
export async function createTinfoilAI(apiKey, options = {}, headers = {}) {
|
|
5
5
|
const baseURL = options.baseURL;
|
|
6
6
|
const enclaveURL = options.enclaveURL;
|
|
7
7
|
const configRepo = options.configRepo || TINFOIL_CONFIG.INFERENCE_PROXY_REPO;
|
|
@@ -21,5 +21,6 @@ export async function createTinfoilAI(apiKey, options = {}) {
|
|
|
21
21
|
baseURL: finalBaseURL,
|
|
22
22
|
apiKey: apiKey,
|
|
23
23
|
fetch: secureClient.fetch,
|
|
24
|
+
headers,
|
|
24
25
|
});
|
|
25
26
|
}
|
|
@@ -82,12 +82,20 @@ export class SecureClient {
|
|
|
82
82
|
releaseDigest: "",
|
|
83
83
|
codeMeasurement: { type: "", registers: [] },
|
|
84
84
|
enclaveMeasurement: { measurement: { type: "", registers: [] } },
|
|
85
|
+
tlsPublicKey: "",
|
|
86
|
+
hpkePublicKey: "",
|
|
87
|
+
hardwareMeasurement: undefined,
|
|
88
|
+
codeFingerprint: "",
|
|
89
|
+
enclaveFingerprint: "",
|
|
90
|
+
selectedRouterEndpoint: new URL(this.enclaveURL).hostname,
|
|
85
91
|
securityVerified: false,
|
|
86
92
|
steps: {
|
|
87
93
|
fetchDigest: { status: "pending" },
|
|
88
94
|
verifyCode: { status: "pending" },
|
|
89
95
|
verifyEnclave: { status: "pending" },
|
|
90
96
|
compareMeasurements: { status: "pending" },
|
|
97
|
+
createTransport: undefined,
|
|
98
|
+
verifyHPKEKey: undefined,
|
|
91
99
|
otherError: { status: "failed", error: error.message },
|
|
92
100
|
},
|
|
93
101
|
};
|
|
@@ -1,22 +1,12 @@
|
|
|
1
1
|
import { createEncryptedBodyFetch } from "./encrypted-body-fetch.mjs";
|
|
2
|
-
import { createPinnedTlsFetch } from "./pinned-tls-fetch.mjs";
|
|
3
|
-
import { isRealBrowser } from "./env.mjs";
|
|
4
2
|
export function createSecureFetch(baseURL, enclaveURL, hpkePublicKey, tlsPublicKeyFingerprint) {
|
|
5
3
|
let fetchFunction;
|
|
6
4
|
if (hpkePublicKey) {
|
|
7
5
|
fetchFunction = createEncryptedBodyFetch(baseURL, hpkePublicKey, enclaveURL);
|
|
8
6
|
}
|
|
9
7
|
else {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
throw new Error("HPKE public key not available and TLS-only verification is not supported in browsers. " +
|
|
13
|
-
"Only HPKE-enabled enclaves can be used in browser environments.");
|
|
14
|
-
}
|
|
15
|
-
// Node.js environment: fall back to TLS-only verification using pinned TLS fetch
|
|
16
|
-
if (!tlsPublicKeyFingerprint) {
|
|
17
|
-
throw new Error("Neither HPKE public key nor TLS public key fingerprint available for verification");
|
|
18
|
-
}
|
|
19
|
-
fetchFunction = createPinnedTlsFetch(baseURL, tlsPublicKeyFingerprint);
|
|
8
|
+
throw new Error("HPKE public key not available and TLS-only verification is not supported in browsers. " +
|
|
9
|
+
"Only HPKE-enabled enclaves can be used in browser environments.");
|
|
20
10
|
}
|
|
21
11
|
return fetchFunction;
|
|
22
12
|
}
|
package/dist/esm/verifier.d.ts
CHANGED
|
@@ -5,6 +5,14 @@ export interface AttestationMeasurement {
|
|
|
5
5
|
type: string;
|
|
6
6
|
registers: string[];
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Hardware measurement from TDX platform verification
|
|
10
|
+
*/
|
|
11
|
+
export interface HardwareMeasurement {
|
|
12
|
+
ID: string;
|
|
13
|
+
MRTD: string;
|
|
14
|
+
RTMR0: string;
|
|
15
|
+
}
|
|
8
16
|
/**
|
|
9
17
|
* Attestation response containing cryptographic keys and measurements
|
|
10
18
|
* At least one of tlsPublicKeyFingerprint or hpkePublicKey must be present
|
|
@@ -31,6 +39,12 @@ export interface VerificationDocument {
|
|
|
31
39
|
releaseDigest: string;
|
|
32
40
|
codeMeasurement: AttestationMeasurement;
|
|
33
41
|
enclaveMeasurement: AttestationResponse;
|
|
42
|
+
tlsPublicKey: string;
|
|
43
|
+
hpkePublicKey: string;
|
|
44
|
+
hardwareMeasurement?: HardwareMeasurement;
|
|
45
|
+
codeFingerprint: string;
|
|
46
|
+
enclaveFingerprint: string;
|
|
47
|
+
selectedRouterEndpoint: string;
|
|
34
48
|
securityVerified: boolean;
|
|
35
49
|
steps: {
|
|
36
50
|
fetchDigest: VerificationStepState;
|
|
@@ -42,29 +56,19 @@ export interface VerificationDocument {
|
|
|
42
56
|
otherError?: VerificationStepState;
|
|
43
57
|
};
|
|
44
58
|
}
|
|
45
|
-
export interface MeasurementComparisonResult {
|
|
46
|
-
match: boolean;
|
|
47
|
-
error?: Error;
|
|
48
|
-
}
|
|
49
|
-
export declare function compareMeasurementsDetailed(codeMeasurement: AttestationMeasurement, runtimeMeasurement: AttestationMeasurement): MeasurementComparisonResult;
|
|
50
|
-
/**
|
|
51
|
-
* Compare two measurements according to platform-specific rules
|
|
52
|
-
* This is predicate function for comparing attestation measurements
|
|
53
|
-
* taken from https://github.com/tinfoilsh/verifier/blob/main/attestation/attestation.go
|
|
54
|
-
*
|
|
55
|
-
* @param codeMeasurement - Expected measurement from code attestation
|
|
56
|
-
* @param runtimeMeasurement - Actual measurement from runtime attestation
|
|
57
|
-
* @returns true if measurements match according to platform rules
|
|
58
|
-
*/
|
|
59
|
-
export declare function compareMeasurements(codeMeasurement: AttestationMeasurement, runtimeMeasurement: AttestationMeasurement): boolean;
|
|
60
59
|
/**
|
|
61
60
|
* Verifier performs attestation verification for Tinfoil enclaves
|
|
62
61
|
*
|
|
63
|
-
* The verifier loads a WebAssembly module that
|
|
62
|
+
* The verifier loads a WebAssembly module (compiled from Go) that performs
|
|
63
|
+
* end-to-end attestation verification:
|
|
64
64
|
* 1. Fetches the latest code release digest from GitHub
|
|
65
|
-
* 2.
|
|
66
|
-
* 3. Performs
|
|
67
|
-
* 4.
|
|
65
|
+
* 2. Verifies code provenance using Sigstore/Rekor
|
|
66
|
+
* 3. Performs runtime attestation against the enclave
|
|
67
|
+
* 4. Verifies hardware measurements (for TDX platforms)
|
|
68
|
+
* 5. Compares code and runtime measurements using platform-specific logic
|
|
69
|
+
*
|
|
70
|
+
* Primary method: verify() - Returns AttestationResponse with cryptographic keys
|
|
71
|
+
* Verification details: getVerificationDocument() - Returns step-by-step results
|
|
68
72
|
*/
|
|
69
73
|
export declare class Verifier {
|
|
70
74
|
private static goInstance;
|
|
@@ -86,56 +90,52 @@ export declare class Verifier {
|
|
|
86
90
|
*/
|
|
87
91
|
private static executeWithWasm;
|
|
88
92
|
/**
|
|
89
|
-
*
|
|
90
|
-
* @param configRepo - Repository name (e.g., "tinfoilsh/confidential-model-router")
|
|
91
|
-
* @returns The digest hash
|
|
92
|
-
*/
|
|
93
|
-
fetchLatestDigest(configRepo?: string): Promise<string>;
|
|
94
|
-
/**
|
|
95
|
-
* Perform runtime attestation on the enclave
|
|
96
|
-
* @param enclaveHost - The enclave hostname
|
|
97
|
-
* @returns Attestation response with measurement and keys
|
|
98
|
-
*/
|
|
99
|
-
verifyEnclave(enclaveHost?: string): Promise<AttestationResponse>;
|
|
100
|
-
/**
|
|
101
|
-
* Perform code attestation
|
|
102
|
-
* @param configRepo - Repository name
|
|
103
|
-
* @param digest - Code digest hash
|
|
104
|
-
* @returns Code measurement
|
|
105
|
-
*/
|
|
106
|
-
verifyCode(configRepo: string, digest: string): Promise<{
|
|
107
|
-
measurement: AttestationMeasurement;
|
|
108
|
-
}>;
|
|
109
|
-
/**
|
|
110
|
-
* Perform attestation verification
|
|
93
|
+
* Perform end-to-end attestation verification
|
|
111
94
|
*
|
|
112
|
-
* This method:
|
|
95
|
+
* This method performs all verification steps atomically via the Go WASM verify() function:
|
|
113
96
|
* 1. Fetches the latest code digest from GitHub releases
|
|
114
|
-
* 2.
|
|
115
|
-
* 3.
|
|
116
|
-
* 4.
|
|
117
|
-
* 5.
|
|
97
|
+
* 2. Verifies code provenance using Sigstore/Rekor
|
|
98
|
+
* 3. Performs runtime attestation against the enclave
|
|
99
|
+
* 4. Verifies hardware measurements (for TDX platforms)
|
|
100
|
+
* 5. Compares code and runtime measurements using platform-specific logic
|
|
118
101
|
*
|
|
119
102
|
* The WASM runtime is automatically initialized and cleaned up within this method.
|
|
103
|
+
* A detailed verification document is saved and can be accessed via getVerificationDocument().
|
|
120
104
|
*
|
|
121
|
-
* @
|
|
105
|
+
* @returns AttestationResponse containing cryptographic keys (TLS/HPKE) and enclave measurement
|
|
106
|
+
* @throws Error if measurements don't match or verification fails at any step
|
|
122
107
|
*/
|
|
123
108
|
verify(): Promise<AttestationResponse>;
|
|
109
|
+
/**
|
|
110
|
+
* Save a failed verification document
|
|
111
|
+
*/
|
|
112
|
+
private saveFailedVerificationDocument;
|
|
124
113
|
/**
|
|
125
114
|
* Internal verification logic that runs within WASM context
|
|
126
115
|
*/
|
|
127
116
|
private verifyInternal;
|
|
128
117
|
/**
|
|
129
|
-
* Returns the
|
|
118
|
+
* Returns the verification document from the last verify() call
|
|
119
|
+
*
|
|
120
|
+
* The document contains detailed step-by-step verification results including:
|
|
121
|
+
* - Step status (pending/success/failed) for each verification phase
|
|
122
|
+
* - Measurements, fingerprints, and cryptographic keys
|
|
123
|
+
* - Error messages for any failed steps
|
|
124
|
+
*
|
|
125
|
+
* Available even if verification failed, allowing inspection of which step failed.
|
|
126
|
+
*
|
|
127
|
+
* @returns VerificationDocument with complete verification details, or undefined if verify() hasn't been called
|
|
130
128
|
*/
|
|
131
129
|
getVerificationDocument(): VerificationDocument | undefined;
|
|
132
130
|
}
|
|
133
131
|
/**
|
|
134
132
|
* Control WASM log output
|
|
135
133
|
*
|
|
136
|
-
* The Go WASM runtime outputs logs through a polyfilled fs.writeSync.
|
|
134
|
+
* The Go WASM runtime outputs logs (stdout/stderr) through a polyfilled fs.writeSync.
|
|
137
135
|
* This function allows suppressing those logs without affecting other console output.
|
|
136
|
+
* By default, WASM logs are suppressed to reduce noise.
|
|
138
137
|
*
|
|
139
138
|
* @param suppress - Whether to suppress WASM logs (default: true)
|
|
139
|
+
* @returns void
|
|
140
140
|
*/
|
|
141
141
|
export declare function suppressWasmLogs(suppress?: boolean): void;
|