Skip to main content

Introduction

The Verification Center is an embeddable iframe that displays the status of Tinfoil’s enclave verification process. It shows users the cryptographic proof that their data is being processed in a verified secure enclave. You can see it live at chat.tinfoil.sh. This guide covers how to embed the Verification Center in your web application and feed it verification data from the Tinfoil JavaScript SDK.

Verification States

The Verification Center displays different states based on the verification results:
When all verification steps pass, users see confirmation that their data is protected:
Verification Center showing successful verification

Prerequisites

Install the Tinfoil JavaScript SDK:
npm install tinfoil

Basic Integration

The integration involves two parts: embedding the iframe and sending it verification data from the SDK.

1. Add the Iframe

<iframe
  id="tinfoil-verification"
  src="https://verification-center.tinfoil.sh"
  style="width: 420px; height: 100vh; border: none;"
  title="Tinfoil Verification Center"
></iframe>

2. Send Verification Data

Use the Tinfoil SDK to get the verification document and send it to the iframe via postMessage:
import { TinfoilAI } from "tinfoil";

const client = new TinfoilAI({ apiKey: "your-api-key" });
await client.ready();

const verificationDoc = await client.getVerificationDocument();
const iframe = document.getElementById("tinfoil-verification") as HTMLIFrameElement;

// Wait for the iframe to signal it's ready
window.addEventListener("message", (event) => {
  if (event.origin !== "https://verification-center.tinfoil.sh") return;

  if (event.data.type === "TINFOIL_VERIFICATION_CENTER_READY") {
    iframe.contentWindow?.postMessage(
      {
        type: "TINFOIL_VERIFICATION_DOCUMENT",
        document: verificationDoc,
      },
      "https://verification-center.tinfoil.sh"
    );
  }
});

URL Parameters

The Verification Center accepts these query parameters:
ParameterTypeDefaultDescription
darkModebooleanfalseEnable dark theme
showHeaderbooleantrueShow or hide the header
Example with dark mode enabled:
<iframe
  src="https://verification-center.tinfoil.sh?darkMode=true&showHeader=true"
  style="width: 420px; height: 100vh; border: none;"
  title="Tinfoil Verification Center"
></iframe>

PostMessage API

The Verification Center communicates with its parent window using the postMessage API.

Messages from the Iframe

Listen for these messages from the Verification Center:
window.addEventListener("message", (event) => {
  // Only handle messages from the Verification Center
  if (event.origin !== "https://verification-center.tinfoil.sh") return;

  switch (event.data.type) {
    case "TINFOIL_VERIFICATION_CENTER_READY":
      // Iframe is ready to receive verification data
      break;
    case "TINFOIL_REQUEST_VERIFICATION_DOCUMENT":
      // Iframe is requesting a fresh verification document
      break;
  }
});

Messages to the Iframe

Send verification data to the iframe:
iframe.contentWindow?.postMessage(
  {
    type: "TINFOIL_VERIFICATION_DOCUMENT",
    document: verificationDoc,
  },
  "https://verification-center.tinfoil.sh"
);

Complete Example

Here’s a complete integration with a sidebar layout:
<!DOCTYPE html>
<html>
<head>
  <style>
    .verification-sidebar {
      position: fixed;
      right: 0;
      top: 0;
      width: 420px;
      height: 100vh;
      transform: translateX(100%);
      transition: transform 200ms ease-in-out;
      border-left: 1px solid #e5e7eb;
      background: white;
      z-index: 40;
    }
    .verification-sidebar.open {
      transform: translateX(0);
    }
    .verification-sidebar iframe {
      width: 100%;
      height: 100%;
      border: none;
    }
  </style>
</head>
<body>
  <button id="verify-btn">Show Verification</button>

  <div id="sidebar" class="verification-sidebar">
    <iframe
      id="tinfoil-verification"
      src="https://verification-center.tinfoil.sh?darkMode=false&showHeader=true"
      title="Tinfoil Verification Center"
    ></iframe>
  </div>

  <script type="module">
    import { TinfoilAI } from "tinfoil";

    const iframe = document.getElementById("tinfoil-verification");
    const sidebar = document.getElementById("sidebar");
    const button = document.getElementById("verify-btn");

    let verificationDoc = null;
    let iframeReady = false;

    // Initialize the Tinfoil client
    const client = new TinfoilAI({ apiKey: "your-api-key" });

    async function initialize() {
      await client.ready();
      verificationDoc = await client.getVerificationDocument();

      // If iframe is already ready, send the document
      if (iframeReady) {
        sendVerificationDocument();
      }
    }

    const VERIFICATION_CENTER_ORIGIN = "https://verification-center.tinfoil.sh";

    function sendVerificationDocument() {
      if (verificationDoc && iframe.contentWindow) {
        iframe.contentWindow.postMessage(
          {
            type: "TINFOIL_VERIFICATION_DOCUMENT",
            document: verificationDoc,
          },
          VERIFICATION_CENTER_ORIGIN
        );
      }
    }

    // Listen for iframe messages
    window.addEventListener("message", (event) => {
      if (event.origin !== VERIFICATION_CENTER_ORIGIN) return;

      if (event.data.type === "TINFOIL_VERIFICATION_CENTER_READY") {
        iframeReady = true;
        sendVerificationDocument();
      }

      if (event.data.type === "TINFOIL_REQUEST_VERIFICATION_DOCUMENT") {
        sendVerificationDocument();
      }
    });

    // Toggle sidebar
    button.addEventListener("click", () => {
      sidebar.classList.toggle("open");
    });

    initialize();
  </script>
</body>
</html>

Understanding the Verification Document

The verification document contains the results of the three-step verification process:
interface VerificationDocument {
  // Repository and enclave information
  configRepo: string;           // GitHub repo (e.g., "tinfoilsh/confidential-deepseek-r1")
  enclaveHost: string;          // Enclave hostname
  releaseDigest: string;        // SHA256 digest of the release

  // Cryptographic measurements
  codeMeasurement: object;      // Measurement from Sigstore verification
  enclaveMeasurement: object;   // Measurement from enclave attestation
  codeFingerprint: string;      // SHA-256 fingerprint of code
  enclaveFingerprint: string;   // SHA-256 fingerprint of enclave

  // Public keys
  tlsPublicKey: string;         // TLS public key fingerprint
  hpkePublicKey: string;        // HPKE public key for encryption

  // Verification status
  securityVerified: boolean;    // True if all checks passed
  steps: {
    fetchDigest: StepState;
    verifyCode: StepState;
    verifyEnclave: StepState;
    compareMeasurements: StepState;
  };
}

interface StepState {
  status: "pending" | "success" | "failed";
  error?: string;
}
The securityVerified field indicates whether all verification steps passed. Individual step statuses are available in the steps object for granular status display.

React Integration

For React applications, create a component that manages the iframe lifecycle:
import { useEffect, useRef, useState } from "react";
import { TinfoilAI } from "tinfoil";

interface VerificationCenterProps {
  apiKey: string;
  darkMode?: boolean;
  showHeader?: boolean;
}

export function VerificationCenter({
  apiKey,
  darkMode = false,
  showHeader = true,
}: VerificationCenterProps) {
  const iframeRef = useRef<HTMLIFrameElement>(null);
  const [verificationDoc, setVerificationDoc] = useState(null);
  const [iframeReady, setIframeReady] = useState(false);

  const iframeUrl = `https://verification-center.tinfoil.sh?darkMode=${darkMode}&showHeader=${showHeader}`;
  const VERIFICATION_CENTER_ORIGIN = "https://verification-center.tinfoil.sh";

  useEffect(() => {
    const client = new TinfoilAI({ apiKey });

    async function initialize() {
      await client.ready();
      const doc = await client.getVerificationDocument();
      setVerificationDoc(doc);
    }

    initialize();
  }, [apiKey]);

  // Send verification document when both iframe is ready and document is available
  useEffect(() => {
    if (iframeReady && verificationDoc && iframeRef.current?.contentWindow) {
      iframeRef.current.contentWindow.postMessage(
        {
          type: "TINFOIL_VERIFICATION_DOCUMENT",
          document: verificationDoc,
        },
        VERIFICATION_CENTER_ORIGIN
      );
    }
  }, [iframeReady, verificationDoc]);

  // Listen for iframe messages
  useEffect(() => {
    function handleMessage(event: MessageEvent) {
      if (event.origin !== VERIFICATION_CENTER_ORIGIN) return;

      if (event.data.type === "TINFOIL_VERIFICATION_CENTER_READY") {
        setIframeReady(true);
      }

      if (event.data.type === "TINFOIL_REQUEST_VERIFICATION_DOCUMENT") {
        if (verificationDoc && iframeRef.current?.contentWindow) {
          iframeRef.current.contentWindow.postMessage(
            {
              type: "TINFOIL_VERIFICATION_DOCUMENT",
              document: verificationDoc,
            },
            VERIFICATION_CENTER_ORIGIN
          );
        }
      }
    }

    window.addEventListener("message", handleMessage);
    return () => window.removeEventListener("message", handleMessage);
  }, [verificationDoc]);

  return (
    <iframe
      ref={iframeRef}
      src={iframeUrl}
      style={{ width: "420px", height: "100%", border: "none" }}
      title="Tinfoil Verification Center"
    />
  );
}

Next Steps