Swift SDK

Swift SDK for Tinfoil’s secure AI inference API
GitHub: tinfoil-swift

Overview

The Tinfoil Swift SDK is a wrapper around the MacPaw OpenAI SDK that provides secure communication with Tinfoil enclaves. It has the same API as the OpenAI SDK with additional security features including automatic verification that the endpoint is running in a secure Tinfoil enclave, TLS certificate pinning, and attestation validation.

Installation

Swift Package Manager

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/tinfoilsh/tinfoil-swift.git", branch: "main")
]

Xcode

  1. Go to File → Add Package Dependencies
  2. Enter the repository URL: https://github.com/tinfoilsh/tinfoil-swift.git
  3. Select the version you want to use
  4. Click “Add Package”

Note: Tinfoil Swift requires the MacPaw OpenAI SDK as a dependency. When you add Tinfoil Swift through Swift Package Manager, the OpenAI SDK will be automatically included.

Requirements

  • iOS 17.0+ / macOS 12.0+
  • Swift 5.9+
  • Xcode 15.0+

Migration from OpenAI

Migrating from OpenAI to Tinfoil is straightforward. The client is designed to be compatible with the MacPaw OpenAI Swift client:

// Before (OpenAI)
- import OpenAI
- let client = OpenAI(apiToken: ProcessInfo.processInfo.environment["OPENAI_API_KEY"] ?? "")

// After (Tinfoil)
+ import TinfoilAI
+ import OpenAI
+ let client = try await TinfoilAI.create(
+     apiKey: ProcessInfo.processInfo.environment["TINFOIL_API_KEY"] ?? "",
+     githubRepo: "tinfoilsh/confidential-llama3-3-70b-prod-prod",
+     enclaveURL: "llama3-3-70b-p.model.tinfoil.sh"
+ )

All method signatures remain the same since TinfoilAI.create() returns a standard OpenAI client with built-in security features.

Usage Examples

The TinfoilAI.create() method returns a standard OpenAI client that’s been configured with secure enclave verification, certificate pinning, and automatic TLS validation.

Chat Completions

import TinfoilAI
import OpenAI

// Create secure client
let client = try await TinfoilAI.create(
    apiKey: ProcessInfo.processInfo.environment["TINFOIL_API_KEY"] ?? "",
    githubRepo: "tinfoilsh/confidential-llama3-3-70b-prod-prod",
    enclaveURL: "llama3-3-70b-p.model.tinfoil.sh"
)

// Create chat completion
let chatQuery = ChatQuery(
    messages: [
        .system(.init(content: .textContent("You are a helpful assistant."))),
        .user(.init(content: .string("Explain quantum computing in simple terms")))
    ],
    model: "llama3-3-70b"
)

let chatResponse = try await client.chats(query: chatQuery)
print(chatResponse.choices.first?.message.content ?? "No response")

Streaming Responses

let chatQuery = ChatQuery(
    messages: [
        .user(.init(content: .string("Tell me a story about AI safety")))
    ],
    model: "llama3-3-70b",
    stream: true
)

// Stream the response
for try await result in client.chatsStream(query: chatQuery) {
    if let choice = result.choices.first,
       let delta = choice.delta.content {
        print("Received: \(delta)")
    }
    
    // Check for completion
    if let finishReason = result.choices.first?.finishReason {
        print("Stream finished with reason: \(finishReason)")
        break
    }
}

Audio Transcription

import Foundation

// Configure for audio model
let client = try await TinfoilAI.create(
    apiKey: ProcessInfo.processInfo.environment["TINFOIL_API_KEY"] ?? "",
    githubRepo: "tinfoilsh/confidential-audio-processing",
    enclaveURL: "audio-processing.model.tinfoil.sh"
)

// Load audio file
let audioURL = URL(fileURLWithPath: "path/to/audio.mp3")
let audioData = try Data(contentsOf: audioURL)

// Create transcription
let transcriptionQuery = AudioTranscriptionQuery(
    file: audioData,
    fileType: .mp3,
    model: "whisper-large-v3-turbo"
)

let transcription = try await client.audioTranscriptions(query: transcriptionQuery)
print(transcription.text)

Embeddings

// Configure for embedding model
let client = try await TinfoilAI.create(
    apiKey: ProcessInfo.processInfo.environment["TINFOIL_API_KEY"] ?? "",
    githubRepo: "tinfoilsh/confidential-nomic-embed-text",
    enclaveURL: "nomic-embed-text.model.tinfoil.sh"
)

// Generate embeddings
let embeddingQuery = EmbeddingsQuery(
    input: .string("The quick brown fox jumps over the lazy dog"),
    model: "nomic-embed-text"
)

let response = try await client.embeddings(query: embeddingQuery)
if let embedding = response.data.first?.embedding {
    print("Embedding dimension: \(embedding.count)")
}

Advanced Features

Non-Blocking Verification

By default, Tinfoil Swift enforces strict security by failing requests when enclave verification or certificate pinning fails. With non-blocking verification, you can allow requests to proceed even if verification fails, while still being notified of the verification status through a callback.

// Set up a callback to handle verification results
let verificationCallback: NonblockingVerification = { verificationPassed in
    if verificationPassed {
        print("✅ Enclave verification passed - connection is secure")
    } else {
        print("❌ Enclave verification failed - connection may not be secure")
        // Handle verification failure (log, alert user, etc.)
    }
}

// Create client with non-blocking verification
let client = try await TinfoilAI.create(
    apiKey: ProcessInfo.processInfo.environment["TINFOIL_API_KEY"] ?? "",
    githubRepo: "tinfoilsh/model-repo",
    enclaveURL: "enclave.example.com",
    nonblockingVerification: verificationCallback
)

// Requests will proceed even if certificate verification fails
let chatQuery = ChatQuery(
    messages: [
        .user(.init(content: .string("Hello, I need a quick response!")))
    ],
    model: "llama3-3-70b"
)

// This request will go through regardless of verification status
let response = try await client.chats(query: chatQuery)
print(response.choices.first?.message.content ?? "No response")

Important Security Warning: When using non-blocking verification, requests will proceed even if enclave verification or certificate pinning fails. Only use this mode when availability is more important than security guarantees.

Error Handling

import TinfoilAI

do {
    let client = try await TinfoilAI.create(
        apiKey: ProcessInfo.processInfo.environment["TINFOIL_API_KEY"] ?? "",
        githubRepo: "tinfoilsh/confidential-llama3-3-70b-prod-prod",
        enclaveURL: "llama3-3-70b-p.model.tinfoil.sh"
    )
    
    let response = try await client.chats(query: chatQuery)
    // Handle response
} catch {
    print("Error creating client or making request: \(error)")
}

Manual Verification and Certificate Pinning

For advanced use cases, you can perform manual verification and use certificate pinning directly:

// Manual verification with progress callbacks
let verificationCallbacks = VerificationCallbacks(
    onCodeVerificationComplete: { result in
        switch result.status {
        case .success(let digest):
            print("Code verification successful: \(digest)")
        case .failure(let error):
            print("Code verification failed: \(error)")
        default:
            break
        }
    },
    onRuntimeVerificationComplete: { result in
        switch result.status {
        case .success(let digest):
            print("Runtime verification successful: \(digest)")
        case .failure(let error):
            print("Runtime verification failed: \(error)")
        default:
            break
        }
    },
    onSecurityCheckComplete: { result in
        switch result.status {
        case .success:
            print("Security check passed: Code and runtime match")
        case .failure(let error):
            print("Security check failed: \(error)")
        default:
            break
        }
    }
)

let secureClient = SecureClient(
    githubRepo: "tinfoilsh/model-repo",
    enclaveURL: "enclave.example.com",
    callbacks: verificationCallbacks
)

let verificationResult = try await secureClient.verify()
if verificationResult.isMatch {
    print("Verification successful!")
    print("Code digest: \(verificationResult.codeDigest)")
    print("Runtime digest: \(verificationResult.runtimeDigest)")
    print("Key fingerprint: \(verificationResult.publicKeyFP)")
} else {
    print("Verification failed: Code and runtime digests do not match")
}

API Documentation

This library is a drop-in replacement for the MacPaw OpenAI Swift client that can be used with Tinfoil. All methods and types are identical. See the MacPaw OpenAI documentation for complete API usage and documentation.