@zeke-02/tinfoil 0.0.2

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.
Files changed (65) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +169 -0
  3. package/dist/__tests__/test-utils.d.ts +1 -0
  4. package/dist/__tests__/test-utils.js +44 -0
  5. package/dist/ai-sdk-provider.d.ts +7 -0
  6. package/dist/ai-sdk-provider.js +23 -0
  7. package/dist/config.d.ts +17 -0
  8. package/dist/config.js +20 -0
  9. package/dist/encrypted-body-fetch.d.ts +8 -0
  10. package/dist/encrypted-body-fetch.js +93 -0
  11. package/dist/env.d.ts +5 -0
  12. package/dist/env.js +20 -0
  13. package/dist/esm/__tests__/test-utils.d.ts +1 -0
  14. package/dist/esm/__tests__/test-utils.js +38 -0
  15. package/dist/esm/ai-sdk-provider.d.ts +7 -0
  16. package/dist/esm/ai-sdk-provider.js +20 -0
  17. package/dist/esm/config.d.ts +17 -0
  18. package/dist/esm/config.js +17 -0
  19. package/dist/esm/encrypted-body-fetch.d.ts +8 -0
  20. package/dist/esm/encrypted-body-fetch.js +86 -0
  21. package/dist/esm/env.d.ts +5 -0
  22. package/dist/esm/env.js +17 -0
  23. package/dist/esm/fetch-adapter.d.ts +21 -0
  24. package/dist/esm/fetch-adapter.js +23 -0
  25. package/dist/esm/index.browser.d.ts +7 -0
  26. package/dist/esm/index.browser.js +8 -0
  27. package/dist/esm/index.d.ts +8 -0
  28. package/dist/esm/index.js +12 -0
  29. package/dist/esm/pinned-tls-fetch.d.ts +1 -0
  30. package/dist/esm/pinned-tls-fetch.js +110 -0
  31. package/dist/esm/secure-client.d.ts +20 -0
  32. package/dist/esm/secure-client.js +123 -0
  33. package/dist/esm/secure-fetch.browser.d.ts +1 -0
  34. package/dist/esm/secure-fetch.browser.js +10 -0
  35. package/dist/esm/secure-fetch.d.ts +1 -0
  36. package/dist/esm/secure-fetch.js +22 -0
  37. package/dist/esm/tinfoilai.d.ts +54 -0
  38. package/dist/esm/tinfoilai.js +134 -0
  39. package/dist/esm/unverified-client.d.ts +18 -0
  40. package/dist/esm/unverified-client.js +33 -0
  41. package/dist/esm/verifier.d.ts +141 -0
  42. package/dist/esm/verifier.js +741 -0
  43. package/dist/esm/wasm-exec.js +668 -0
  44. package/dist/fetch-adapter.d.ts +21 -0
  45. package/dist/fetch-adapter.js +27 -0
  46. package/dist/index.browser.d.ts +7 -0
  47. package/dist/index.browser.js +29 -0
  48. package/dist/index.d.ts +8 -0
  49. package/dist/index.js +49 -0
  50. package/dist/pinned-tls-fetch.d.ts +1 -0
  51. package/dist/pinned-tls-fetch.js +116 -0
  52. package/dist/secure-client.d.ts +20 -0
  53. package/dist/secure-client.js +127 -0
  54. package/dist/secure-fetch.browser.d.ts +1 -0
  55. package/dist/secure-fetch.browser.js +13 -0
  56. package/dist/secure-fetch.d.ts +1 -0
  57. package/dist/secure-fetch.js +25 -0
  58. package/dist/tinfoilai.d.ts +54 -0
  59. package/dist/tinfoilai.js +141 -0
  60. package/dist/unverified-client.d.ts +18 -0
  61. package/dist/unverified-client.js +37 -0
  62. package/dist/verifier.d.ts +141 -0
  63. package/dist/verifier.js +781 -0
  64. package/dist/wasm-exec.js +668 -0
  65. package/package.json +97 -0
@@ -0,0 +1,7 @@
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";
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.UnverifiedClient = exports.SecureClient = exports.default = exports.TinfoilAI = void 0;
18
+ // Browser-safe entry point: avoids Node built-ins
19
+ var tinfoilai_1 = require("./tinfoilai");
20
+ Object.defineProperty(exports, "TinfoilAI", { enumerable: true, get: function () { return tinfoilai_1.TinfoilAI; } });
21
+ var tinfoilai_2 = require("./tinfoilai");
22
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return tinfoilai_2.TinfoilAI; } });
23
+ __exportStar(require("./verifier"), exports);
24
+ __exportStar(require("./ai-sdk-provider"), exports);
25
+ __exportStar(require("./config"), exports);
26
+ var secure_client_1 = require("./secure-client");
27
+ Object.defineProperty(exports, "SecureClient", { enumerable: true, get: function () { return secure_client_1.SecureClient; } });
28
+ var unverified_client_1 = require("./unverified-client");
29
+ Object.defineProperty(exports, "UnverifiedClient", { enumerable: true, get: function () { return unverified_client_1.UnverifiedClient; } });
@@ -0,0 +1,8 @@
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 { type Uploadable, toFile, APIPromise, PagePromise, OpenAIError, APIError, APIConnectionError, APIConnectionTimeoutError, APIUserAbortError, NotFoundError, ConflictError, RateLimitError, BadRequestError, AuthenticationError, InternalServerError, PermissionDeniedError, UnprocessableEntityError, } from "openai";
package/dist/index.js ADDED
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.UnprocessableEntityError = exports.PermissionDeniedError = exports.InternalServerError = exports.AuthenticationError = exports.BadRequestError = exports.RateLimitError = exports.ConflictError = exports.NotFoundError = exports.APIUserAbortError = exports.APIConnectionTimeoutError = exports.APIConnectionError = exports.APIError = exports.OpenAIError = exports.PagePromise = exports.APIPromise = exports.toFile = exports.UnverifiedClient = exports.SecureClient = exports.default = exports.TinfoilAI = void 0;
18
+ // Re-export the TinfoilAI class
19
+ var tinfoilai_1 = require("./tinfoilai");
20
+ Object.defineProperty(exports, "TinfoilAI", { enumerable: true, get: function () { return tinfoilai_1.TinfoilAI; } });
21
+ var tinfoilai_2 = require("./tinfoilai");
22
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return tinfoilai_2.TinfoilAI; } });
23
+ // Export verifier
24
+ __exportStar(require("./verifier"), exports);
25
+ __exportStar(require("./ai-sdk-provider"), exports);
26
+ __exportStar(require("./config"), exports);
27
+ var secure_client_1 = require("./secure-client");
28
+ Object.defineProperty(exports, "SecureClient", { enumerable: true, get: function () { return secure_client_1.SecureClient; } });
29
+ var unverified_client_1 = require("./unverified-client");
30
+ Object.defineProperty(exports, "UnverifiedClient", { enumerable: true, get: function () { return unverified_client_1.UnverifiedClient; } });
31
+ // Re-export OpenAI utility types and classes that users might need
32
+ // Using public exports from the main OpenAI package instead of deep imports
33
+ var openai_1 = require("openai");
34
+ Object.defineProperty(exports, "toFile", { enumerable: true, get: function () { return openai_1.toFile; } });
35
+ Object.defineProperty(exports, "APIPromise", { enumerable: true, get: function () { return openai_1.APIPromise; } });
36
+ Object.defineProperty(exports, "PagePromise", { enumerable: true, get: function () { return openai_1.PagePromise; } });
37
+ Object.defineProperty(exports, "OpenAIError", { enumerable: true, get: function () { return openai_1.OpenAIError; } });
38
+ Object.defineProperty(exports, "APIError", { enumerable: true, get: function () { return openai_1.APIError; } });
39
+ Object.defineProperty(exports, "APIConnectionError", { enumerable: true, get: function () { return openai_1.APIConnectionError; } });
40
+ Object.defineProperty(exports, "APIConnectionTimeoutError", { enumerable: true, get: function () { return openai_1.APIConnectionTimeoutError; } });
41
+ Object.defineProperty(exports, "APIUserAbortError", { enumerable: true, get: function () { return openai_1.APIUserAbortError; } });
42
+ Object.defineProperty(exports, "NotFoundError", { enumerable: true, get: function () { return openai_1.NotFoundError; } });
43
+ Object.defineProperty(exports, "ConflictError", { enumerable: true, get: function () { return openai_1.ConflictError; } });
44
+ Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return openai_1.RateLimitError; } });
45
+ Object.defineProperty(exports, "BadRequestError", { enumerable: true, get: function () { return openai_1.BadRequestError; } });
46
+ Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return openai_1.AuthenticationError; } });
47
+ Object.defineProperty(exports, "InternalServerError", { enumerable: true, get: function () { return openai_1.InternalServerError; } });
48
+ Object.defineProperty(exports, "PermissionDeniedError", { enumerable: true, get: function () { return openai_1.PermissionDeniedError; } });
49
+ Object.defineProperty(exports, "UnprocessableEntityError", { enumerable: true, get: function () { return openai_1.UnprocessableEntityError; } });
@@ -0,0 +1 @@
1
+ export declare function createPinnedTlsFetch(baseURL: string, expectedFingerprintHex: string): typeof fetch;
@@ -0,0 +1,116 @@
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.createPinnedTlsFetch = createPinnedTlsFetch;
7
+ const https_1 = __importDefault(require("https"));
8
+ const tls_1 = require("tls");
9
+ const crypto_1 = require("crypto");
10
+ const stream_1 = require("stream");
11
+ function createPinnedTlsFetch(baseURL, expectedFingerprintHex) {
12
+ return (async (input, init) => {
13
+ // Normalize URL with base URL support
14
+ const makeURL = (value) => {
15
+ if (typeof value === "string")
16
+ return new URL(value, baseURL);
17
+ if (value instanceof URL)
18
+ return value;
19
+ return new URL(value.url, baseURL);
20
+ };
21
+ const url = makeURL(input);
22
+ if (url.protocol !== "https:") {
23
+ throw new Error(`HTTP connections are not allowed. Use HTTPS. URL: ${url.toString()}`);
24
+ }
25
+ // Gather method and headers
26
+ const method = (init?.method || input.method || "GET").toUpperCase();
27
+ const headers = new Headers(init?.headers || input?.headers || {});
28
+ const headerObj = {};
29
+ headers.forEach((v, k) => {
30
+ headerObj[k] = v;
31
+ });
32
+ // Resolve body
33
+ let body = init?.body;
34
+ if (!body && input instanceof Request) {
35
+ // If the original was a Request with a body, read it
36
+ try {
37
+ const buf = await input.arrayBuffer();
38
+ if (buf && buf.byteLength)
39
+ body = Buffer.from(buf);
40
+ }
41
+ catch { }
42
+ }
43
+ // Convert web streams to Node streams if needed
44
+ if (body && typeof body.getReader === "function") {
45
+ body = stream_1.Readable.fromWeb(body);
46
+ }
47
+ if (body instanceof ArrayBuffer) {
48
+ body = Buffer.from(body);
49
+ }
50
+ if (ArrayBuffer.isView(body)) {
51
+ body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
52
+ }
53
+ const requestOptions = {
54
+ protocol: url.protocol,
55
+ hostname: url.hostname,
56
+ port: url.port ? Number(url.port) : 443,
57
+ path: `${url.pathname}${url.search}`,
58
+ method,
59
+ headers: headerObj,
60
+ checkServerIdentity: (host, cert) => {
61
+ const raw = cert.raw;
62
+ if (!raw) {
63
+ return new Error("Certificate raw bytes are unavailable for pinning");
64
+ }
65
+ const x509 = new crypto_1.X509Certificate(raw);
66
+ const publicKeyDer = x509.publicKey.export({ type: "spki", format: "der" });
67
+ const fp = (0, crypto_1.createHash)("sha256").update(publicKeyDer).digest("hex");
68
+ if (fp !== expectedFingerprintHex) {
69
+ return new Error(`Certificate public key fingerprint mismatch. Expected: ${expectedFingerprintHex}, Got: ${fp}`);
70
+ }
71
+ return (0, tls_1.checkServerIdentity)(host, cert);
72
+ },
73
+ };
74
+ const { signal } = init || {};
75
+ const res = await new Promise((resolve, reject) => {
76
+ const req = https_1.default.request(requestOptions, resolve);
77
+ req.on("error", reject);
78
+ if (signal) {
79
+ if (signal.aborted) {
80
+ req.destroy(new Error("Request aborted"));
81
+ return;
82
+ }
83
+ signal.addEventListener("abort", () => req.destroy(new Error("Request aborted")));
84
+ }
85
+ if (body === undefined || body === null) {
86
+ req.end();
87
+ }
88
+ else if (typeof body === "string" || Buffer.isBuffer(body) || ArrayBuffer.isView(body)) {
89
+ req.end(body);
90
+ }
91
+ else if (typeof body.pipe === "function") {
92
+ body.pipe(req);
93
+ }
94
+ else {
95
+ // Fallback: try to serialize objects
96
+ req.end(String(body));
97
+ }
98
+ });
99
+ const responseHeaders = new Headers();
100
+ for (const [k, v] of Object.entries(res.headers)) {
101
+ if (Array.isArray(v)) {
102
+ v.forEach(item => responseHeaders.append(k, item));
103
+ }
104
+ else if (v != null) {
105
+ responseHeaders.set(k, String(v));
106
+ }
107
+ }
108
+ // Convert Node stream to Web ReadableStream
109
+ const webStream = stream_1.Readable.toWeb(res);
110
+ return new Response(webStream, {
111
+ status: res.statusCode || 0,
112
+ statusText: res.statusMessage || "",
113
+ headers: responseHeaders,
114
+ });
115
+ });
116
+ }
@@ -0,0 +1,20 @@
1
+ import type { VerificationDocument } from "./verifier";
2
+ interface SecureClientOptions {
3
+ baseURL?: string;
4
+ enclaveURL?: string;
5
+ configRepo?: string;
6
+ }
7
+ export declare class SecureClient {
8
+ private initPromise;
9
+ private verificationDocument;
10
+ private _fetch;
11
+ private readonly baseURL?;
12
+ private readonly enclaveURL?;
13
+ private readonly configRepo?;
14
+ constructor(options?: SecureClientOptions);
15
+ ready(): Promise<void>;
16
+ private initSecureClient;
17
+ getVerificationDocument(): Promise<VerificationDocument>;
18
+ get fetch(): typeof fetch;
19
+ }
20
+ export {};
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SecureClient = void 0;
4
+ const verifier_1 = require("./verifier");
5
+ const config_1 = require("./config");
6
+ const secure_fetch_1 = require("./secure-fetch");
7
+ class SecureClient {
8
+ constructor(options = {}) {
9
+ this.initPromise = null;
10
+ this.verificationDocument = null;
11
+ this._fetch = null;
12
+ this.baseURL = options.baseURL || config_1.TINFOIL_CONFIG.INFERENCE_BASE_URL;
13
+ this.enclaveURL = options.enclaveURL || config_1.TINFOIL_CONFIG.ENCLAVE_URL;
14
+ this.configRepo = options.configRepo || config_1.TINFOIL_CONFIG.INFERENCE_PROXY_REPO;
15
+ }
16
+ async ready() {
17
+ if (!this.initPromise) {
18
+ this.initPromise = this.initSecureClient();
19
+ }
20
+ return this.initPromise;
21
+ }
22
+ async initSecureClient() {
23
+ const verifier = new verifier_1.Verifier({
24
+ serverURL: this.enclaveURL,
25
+ configRepo: this.configRepo,
26
+ });
27
+ try {
28
+ await verifier.verify();
29
+ const doc = verifier.getVerificationDocument();
30
+ if (!doc) {
31
+ throw new Error("Verification document not available after successful verification");
32
+ }
33
+ this.verificationDocument = doc;
34
+ // Extract keys from the verification document
35
+ const { hpkePublicKey, tlsPublicKeyFingerprint } = this.verificationDocument.enclaveMeasurement;
36
+ try {
37
+ this._fetch = (0, secure_fetch_1.createSecureFetch)(this.baseURL, this.enclaveURL, hpkePublicKey, tlsPublicKeyFingerprint);
38
+ }
39
+ catch (transportError) {
40
+ this.verificationDocument.steps.createTransport = {
41
+ status: "failed",
42
+ error: transportError.message,
43
+ };
44
+ this.verificationDocument.securityVerified = false;
45
+ throw transportError;
46
+ }
47
+ }
48
+ catch (error) {
49
+ const doc = verifier.getVerificationDocument();
50
+ if (doc) {
51
+ this.verificationDocument = doc;
52
+ }
53
+ else {
54
+ this.verificationDocument = {
55
+ configRepo: this.configRepo,
56
+ enclaveHost: new URL(this.enclaveURL).hostname,
57
+ releaseDigest: "",
58
+ codeMeasurement: { type: "", registers: [] },
59
+ enclaveMeasurement: { measurement: { type: "", registers: [] } },
60
+ securityVerified: false,
61
+ steps: {
62
+ fetchDigest: { status: "pending" },
63
+ verifyCode: { status: "pending" },
64
+ verifyEnclave: { status: "pending" },
65
+ compareMeasurements: { status: "pending" },
66
+ otherError: { status: "failed", error: error.message },
67
+ },
68
+ };
69
+ }
70
+ throw error;
71
+ }
72
+ }
73
+ async getVerificationDocument() {
74
+ if (!this.initPromise) {
75
+ await this.ready();
76
+ }
77
+ await this.initPromise.catch(() => { });
78
+ if (!this.verificationDocument) {
79
+ throw new Error("Verification document unavailable: client not verified yet");
80
+ }
81
+ return this.verificationDocument;
82
+ }
83
+ get fetch() {
84
+ return async (input, init) => {
85
+ await this.ready();
86
+ try {
87
+ return await this._fetch(input, init);
88
+ }
89
+ catch (error) {
90
+ if (this.verificationDocument) {
91
+ const errorMessage = error.message;
92
+ if (errorMessage.includes("HPKE public key mismatch")) {
93
+ this.verificationDocument.steps.verifyHPKEKey = {
94
+ status: "failed",
95
+ error: errorMessage,
96
+ };
97
+ this.verificationDocument.securityVerified = false;
98
+ }
99
+ else if (errorMessage.includes("Transport initialization failed") ||
100
+ errorMessage.includes("Request initialization failed")) {
101
+ this.verificationDocument.steps.createTransport = {
102
+ status: "failed",
103
+ error: errorMessage,
104
+ };
105
+ this.verificationDocument.securityVerified = false;
106
+ }
107
+ else if (errorMessage.includes("Failed to get HPKE key")) {
108
+ this.verificationDocument.steps.verifyHPKEKey = {
109
+ status: "failed",
110
+ error: errorMessage,
111
+ };
112
+ this.verificationDocument.securityVerified = false;
113
+ }
114
+ else {
115
+ this.verificationDocument.steps.otherError = {
116
+ status: "failed",
117
+ error: errorMessage,
118
+ };
119
+ this.verificationDocument.securityVerified = false;
120
+ }
121
+ }
122
+ throw error;
123
+ }
124
+ };
125
+ }
126
+ }
127
+ exports.SecureClient = SecureClient;
@@ -0,0 +1 @@
1
+ export declare function createSecureFetch(baseURL: string, enclaveURL?: string, hpkePublicKey?: string, tlsPublicKeyFingerprint?: string): typeof fetch;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSecureFetch = createSecureFetch;
4
+ const encrypted_body_fetch_1 = require("./encrypted-body-fetch");
5
+ function createSecureFetch(baseURL, enclaveURL, hpkePublicKey, tlsPublicKeyFingerprint) {
6
+ if (hpkePublicKey) {
7
+ return (0, encrypted_body_fetch_1.createEncryptedBodyFetch)(baseURL, hpkePublicKey, enclaveURL);
8
+ }
9
+ else {
10
+ throw new Error("HPKE public key not available and TLS-only verification is not supported in browsers. " +
11
+ "Only HPKE-enabled enclaves can be used in browser environments.");
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ export declare function createSecureFetch(baseURL: string, enclaveURL?: string, hpkePublicKey?: string, tlsPublicKeyFingerprint?: string): typeof fetch;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSecureFetch = createSecureFetch;
4
+ const encrypted_body_fetch_1 = require("./encrypted-body-fetch");
5
+ const pinned_tls_fetch_1 = require("./pinned-tls-fetch");
6
+ const env_1 = require("./env");
7
+ function createSecureFetch(baseURL, enclaveURL, hpkePublicKey, tlsPublicKeyFingerprint) {
8
+ let fetchFunction;
9
+ if (hpkePublicKey) {
10
+ fetchFunction = (0, encrypted_body_fetch_1.createEncryptedBodyFetch)(baseURL, hpkePublicKey, enclaveURL);
11
+ }
12
+ else {
13
+ // HPKE not available: check if we're in a browser
14
+ if ((0, env_1.isRealBrowser)()) {
15
+ throw new Error("HPKE public key not available and TLS-only verification is not supported in browsers. " +
16
+ "Only HPKE-enabled enclaves can be used in browser environments.");
17
+ }
18
+ // Node.js environment: fall back to TLS-only verification using pinned TLS fetch
19
+ if (!tlsPublicKeyFingerprint) {
20
+ throw new Error("Neither HPKE public key nor TLS public key fingerprint available for verification");
21
+ }
22
+ fetchFunction = (0, pinned_tls_fetch_1.createPinnedTlsFetch)(baseURL, tlsPublicKeyFingerprint);
23
+ }
24
+ return fetchFunction;
25
+ }
@@ -0,0 +1,54 @@
1
+ import OpenAI from "openai";
2
+ import type { Audio, Beta, Chat, Embeddings, Files, FineTuning, Images, Models, Moderations, Responses } from "openai/resources";
3
+ import type { VerificationDocument } from "./verifier";
4
+ interface TinfoilAIOptions {
5
+ apiKey?: string;
6
+ baseURL?: string;
7
+ enclaveURL?: string;
8
+ configRepo?: string;
9
+ [key: string]: any;
10
+ }
11
+ export declare class TinfoilAI {
12
+ private client?;
13
+ private clientPromise;
14
+ private readyPromise?;
15
+ private configRepo?;
16
+ private secureClient;
17
+ private verificationDocument?;
18
+ apiKey?: string;
19
+ baseURL?: string;
20
+ enclaveURL?: string;
21
+ constructor(options?: TinfoilAIOptions);
22
+ ready(): Promise<void>;
23
+ private initClient;
24
+ private createOpenAIClient;
25
+ private ensureReady;
26
+ getVerificationDocument(): Promise<VerificationDocument>;
27
+ get chat(): Chat;
28
+ get files(): Files;
29
+ get fineTuning(): FineTuning;
30
+ get images(): Images;
31
+ get audio(): Audio;
32
+ get responses(): Responses;
33
+ get embeddings(): Embeddings;
34
+ get models(): Models;
35
+ get moderations(): Moderations;
36
+ get beta(): Beta;
37
+ }
38
+ export declare namespace TinfoilAI {
39
+ export import Chat = OpenAI.Chat;
40
+ export import Audio = OpenAI.Audio;
41
+ export import Beta = OpenAI.Beta;
42
+ export import Batches = OpenAI.Batches;
43
+ export import Completions = OpenAI.Completions;
44
+ export import Embeddings = OpenAI.Embeddings;
45
+ export import Files = OpenAI.Files;
46
+ export import FineTuning = OpenAI.FineTuning;
47
+ export import Images = OpenAI.Images;
48
+ export import Models = OpenAI.Models;
49
+ export import Moderations = OpenAI.Moderations;
50
+ export import Responses = OpenAI.Responses;
51
+ export import Uploads = OpenAI.Uploads;
52
+ export import VectorStores = OpenAI.VectorStores;
53
+ }
54
+ export {};
@@ -0,0 +1,141 @@
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.TinfoilAI = void 0;
7
+ const openai_1 = __importDefault(require("openai"));
8
+ const secure_client_1 = require("./secure-client");
9
+ const config_1 = require("./config");
10
+ const env_1 = require("./env");
11
+ function createAsyncProxy(promise) {
12
+ return new Proxy({}, {
13
+ get(target, prop) {
14
+ return new Proxy(() => { }, {
15
+ get(_, nestedProp) {
16
+ return (...args) => promise.then((obj) => {
17
+ const value = obj[prop][nestedProp];
18
+ return typeof value === "function"
19
+ ? value.apply(obj[prop], args)
20
+ : value;
21
+ });
22
+ },
23
+ apply(_, __, args) {
24
+ return promise.then((obj) => {
25
+ const value = obj[prop];
26
+ return typeof value === "function" ? value.apply(obj, args) : value;
27
+ });
28
+ },
29
+ });
30
+ },
31
+ });
32
+ }
33
+ class TinfoilAI {
34
+ constructor(options = {}) {
35
+ const openAIOptions = { ...options };
36
+ // In browser builds, never read secrets from process.env to avoid
37
+ // leaking credentials into client bundles. Require explicit apiKey.
38
+ if (options.apiKey) {
39
+ openAIOptions.apiKey = options.apiKey;
40
+ }
41
+ else if (!(0, env_1.isRealBrowser)() && process.env.TINFOIL_API_KEY) {
42
+ openAIOptions.apiKey = process.env.TINFOIL_API_KEY;
43
+ }
44
+ this.apiKey = openAIOptions.apiKey;
45
+ this.baseURL = options.baseURL || config_1.TINFOIL_CONFIG.INFERENCE_BASE_URL;
46
+ this.enclaveURL = options.enclaveURL || config_1.TINFOIL_CONFIG.ENCLAVE_URL;
47
+ this.configRepo = options.configRepo || config_1.TINFOIL_CONFIG.INFERENCE_PROXY_REPO;
48
+ this.secureClient = new secure_client_1.SecureClient({
49
+ baseURL: this.baseURL,
50
+ enclaveURL: this.enclaveURL,
51
+ configRepo: this.configRepo,
52
+ });
53
+ this.clientPromise = this.initClient(openAIOptions);
54
+ }
55
+ async ready() {
56
+ if (!this.readyPromise) {
57
+ this.readyPromise = (async () => {
58
+ this.client = await this.clientPromise;
59
+ })();
60
+ }
61
+ return this.readyPromise;
62
+ }
63
+ async initClient(options) {
64
+ return this.createOpenAIClient(options);
65
+ }
66
+ async createOpenAIClient(options = {}) {
67
+ await this.secureClient.ready();
68
+ this.verificationDocument = await this.secureClient.getVerificationDocument();
69
+ if (!this.verificationDocument) {
70
+ throw new Error("Verification document not available after successful verification");
71
+ }
72
+ const clientOptions = {
73
+ ...options,
74
+ baseURL: this.baseURL,
75
+ fetch: this.secureClient.fetch,
76
+ };
77
+ if ((0, env_1.isRealBrowser)() || options.dangerouslyAllowBrowser === true) {
78
+ clientOptions.dangerouslyAllowBrowser = true;
79
+ }
80
+ return new openai_1.default(clientOptions);
81
+ }
82
+ async ensureReady() {
83
+ await this.ready();
84
+ return this.client;
85
+ }
86
+ async getVerificationDocument() {
87
+ await this.ready();
88
+ if (!this.verificationDocument) {
89
+ throw new Error("Verification document unavailable: client not verified yet");
90
+ }
91
+ return this.verificationDocument;
92
+ }
93
+ get chat() {
94
+ return createAsyncProxy(this.ensureReady().then((client) => client.chat));
95
+ }
96
+ get files() {
97
+ return createAsyncProxy(this.ensureReady().then((client) => client.files));
98
+ }
99
+ get fineTuning() {
100
+ return createAsyncProxy(this.ensureReady().then((client) => client.fineTuning));
101
+ }
102
+ get images() {
103
+ return createAsyncProxy(this.ensureReady().then((client) => client.images));
104
+ }
105
+ get audio() {
106
+ return createAsyncProxy(this.ensureReady().then((client) => client.audio));
107
+ }
108
+ get responses() {
109
+ return createAsyncProxy(this.ensureReady().then((client) => client.responses));
110
+ }
111
+ get embeddings() {
112
+ return createAsyncProxy(this.ensureReady().then((client) => client.embeddings));
113
+ }
114
+ get models() {
115
+ return createAsyncProxy(this.ensureReady().then((client) => client.models));
116
+ }
117
+ get moderations() {
118
+ return createAsyncProxy(this.ensureReady().then((client) => client.moderations));
119
+ }
120
+ get beta() {
121
+ return createAsyncProxy(this.ensureReady().then((client) => client.beta));
122
+ }
123
+ }
124
+ exports.TinfoilAI = TinfoilAI;
125
+ // Namespace declaration merge to add OpenAI types to TinfoilAI
126
+ (function (TinfoilAI) {
127
+ TinfoilAI.Chat = openai_1.default.Chat;
128
+ TinfoilAI.Audio = openai_1.default.Audio;
129
+ TinfoilAI.Beta = openai_1.default.Beta;
130
+ TinfoilAI.Batches = openai_1.default.Batches;
131
+ TinfoilAI.Completions = openai_1.default.Completions;
132
+ TinfoilAI.Embeddings = openai_1.default.Embeddings;
133
+ TinfoilAI.Files = openai_1.default.Files;
134
+ TinfoilAI.FineTuning = openai_1.default.FineTuning;
135
+ TinfoilAI.Images = openai_1.default.Images;
136
+ TinfoilAI.Models = openai_1.default.Models;
137
+ TinfoilAI.Moderations = openai_1.default.Moderations;
138
+ TinfoilAI.Responses = openai_1.default.Responses;
139
+ TinfoilAI.Uploads = openai_1.default.Uploads;
140
+ TinfoilAI.VectorStores = openai_1.default.VectorStores;
141
+ })(TinfoilAI || (exports.TinfoilAI = TinfoilAI = {}));
@@ -0,0 +1,18 @@
1
+ interface UnverifiedClientOptions {
2
+ baseURL?: string;
3
+ enclaveURL?: string;
4
+ configRepo?: string;
5
+ }
6
+ export declare class UnverifiedClient {
7
+ private initPromise;
8
+ private _fetch;
9
+ private readonly baseURL;
10
+ private readonly enclaveURL;
11
+ private readonly configRepo;
12
+ constructor(options?: UnverifiedClientOptions);
13
+ ready(): Promise<void>;
14
+ private initUnverifiedClient;
15
+ getVerificationDocument(): Promise<void>;
16
+ get fetch(): typeof fetch;
17
+ }
18
+ export {};
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnverifiedClient = void 0;
4
+ const config_1 = require("./config");
5
+ const encrypted_body_fetch_1 = require("./encrypted-body-fetch");
6
+ class UnverifiedClient {
7
+ constructor(options = {}) {
8
+ this.initPromise = null;
9
+ this._fetch = null;
10
+ this.baseURL = options.baseURL || config_1.TINFOIL_CONFIG.INFERENCE_BASE_URL;
11
+ this.enclaveURL = options.enclaveURL || config_1.TINFOIL_CONFIG.ENCLAVE_URL;
12
+ this.configRepo = options.configRepo || config_1.TINFOIL_CONFIG.INFERENCE_PROXY_REPO;
13
+ }
14
+ async ready() {
15
+ if (!this.initPromise) {
16
+ this.initPromise = this.initUnverifiedClient();
17
+ }
18
+ return this.initPromise;
19
+ }
20
+ async initUnverifiedClient() {
21
+ this._fetch = (0, encrypted_body_fetch_1.createEncryptedBodyFetch)(this.baseURL, undefined, this.enclaveURL);
22
+ }
23
+ async getVerificationDocument() {
24
+ if (!this.initPromise) {
25
+ await this.ready();
26
+ }
27
+ await this.initPromise;
28
+ throw new Error("Verification document unavailable: this version of the client is unverified");
29
+ }
30
+ get fetch() {
31
+ return async (input, init) => {
32
+ await this.ready();
33
+ return this._fetch(input, init);
34
+ };
35
+ }
36
+ }
37
+ exports.UnverifiedClient = UnverifiedClient;