This is page 1 of 2. Use http://codebase.md/0xfreysa/trusted-mcp-server?page={x} to view the full context.
# Directory Structure
```
├── Dockerfile
├── gmail_mcp
│   ├── pyproject.toml
│   └── server.py
├── gvproxy.sh
├── Makefile
├── README.md
├── run-enclave.sh
├── setup.sh
├── start.sh
└── verifier
    ├── mcp
    │   └── react-ts-webpack
    │       ├── index.ejs
    │       ├── package.json
    │       ├── pnpm-lock.yaml
    │       ├── postcss.config.js
    │       ├── README.md
    │       ├── src
    │       │   ├── app.css
    │       │   ├── app.tsx
    │       │   ├── components
    │       │   │   ├── alert.tsx
    │       │   │   ├── button.tsx
    │       │   │   ├── shadcn.tsx
    │       │   │   ├── verify-mcp.tsx
    │       │   │   └── verify-tee.tsx
    │       │   └── utils
    │       │       ├── misc.ts
    │       │       ├── requests.ts
    │       │       └── worker.ts
    │       ├── tailwind.config.js
    │       ├── tsconfig.json
    │       └── webpack.js
    ├── package.json
    ├── pnpm-lock.yaml
    ├── rust-toolchain
    ├── serve.json
    ├── src
    │   ├── lib.ts
    │   ├── types.ts
    │   └── utils.ts
    ├── test
    │   ├── assets
    │   │   ├── notary.pem
    │   │   ├── simple_proof_expected.json
    │   │   └── simple_proof_redacted.json
    │   ├── specs
    │   │   ├── full-integration-swapi.spec.ts
    │   │   └── simple-verify.spec.ts
    │   ├── test.ejs
    │   ├── testRunner.ts
    │   ├── utils.ts
    │   └── worker.ts
    ├── tsconfig.compile.json
    ├── tsconfig.json
    ├── utils
    │   ├── build-tlsn-binaries.sh
    │   └── check-wasm.sh
    ├── wasm
    │   ├── pkg
    │   │   ├── package.json
    │   │   ├── README.md
    │   │   ├── snippets
    │   │   │   ├── wasm-bindgen-futures-a509390b5b548b61
    │   │   │   │   └── src
    │   │   │   │       └── task
    │   │   │   │           └── worker.js
    │   │   │   └── wasm-bindgen-rayon-3e04391371ad0a8e
    │   │   │       └── src
    │   │   │           ├── workerHelpers.js
    │   │   │           └── workerHelpers.worker.js
    │   │   ├── tlsn_wasm_bg.wasm
    │   │   ├── tlsn_wasm_bg.wasm.d.ts
    │   │   ├── tlsn_wasm.d.ts
    │   │   └── tlsn_wasm.js
    │   └── remote-attestation-verifier
    │       ├── package.json
    │       ├── remote_attestation_verifier_bg.wasm
    │       ├── remote_attestation_verifier_bg.wasm.d.ts
    │       ├── remote_attestation_verifier.d.ts
    │       └── remote_attestation_verifier.js
    ├── webpack.build.config.js
    └── webpack.web.dev.config.js
```
# Files
--------------------------------------------------------------------------------
/verifier/wasm/pkg/README.md:
--------------------------------------------------------------------------------
```markdown
# TLSNotary WASM bindings
## Build
This crate must be built using the nightly rust compiler with the following flags:
```bash
RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals' \
    rustup run nightly \
    wasm-pack build --target web . -- -Zbuild-std=panic_abort,std
```
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/README.md:
--------------------------------------------------------------------------------
```markdown
# React Demo for Notarization and Verification of attestations
This webapp allows you to
1) Generate an attestation for a dummmy request
2) Verifies the attestation
## 🔴 Todo and bugs
### Notarization
- Only works with local websockify server
### Verification
- Notary pubkey is hardcoded, should be retrieved from the notary URL and converted from PEM to raw bytes ASN1
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# Trusted GMail MCP Server
This is a gmail [MCP](https://modelcontextprotocol.io/introduction) server running inside a secure [AWS Nitro](https://aws.amazon.com/ec2/nitro/) enclave instance. It was originally forked from the [Claude Post](https://github.com/ZilongXue/claude-post) MCP server. Most MCP servers are run locally via the `stdio` transport; we followed [this guide](https://www.ragie.ai/blog/building-a-server-sent-events-sse-mcp-server-with-fastapi) to implement a remote MCP server using `sse` transport.
## Connect to the MCP Server
To use this MCP server, you will need an [app-specific password](https://myaccount.google.com/apppasswords).
Then simply add the following block to your client's `mcp.json` file.
```json
    "gmail_mcp": {
      "url": "https://gmail.mcp.freysa.ai/sse/?ADDR=<[email protected]>&ASP=<your app-specific password>"
    }
```
Note that you might have to restart your client.
## Security Notice
This implementation is a proof of concept. Passing app-specific passwords in URLs is not a secure pattern because:
- URLs can be logged by proxies, browsers and servers
- URLs may appear in browser history
- URLs can be leaked via the Referer header to third-party sites
Unfortunately, current MCP clients have limitations on how they connect to servers. At the moment of release, MCP specification does not define a standard authentication mechanism for SSE servers. This means we can't use more secure patterns like bearer tokens or other authorization headers that would normally be preferred.
For additional security, consider:
1. Using a dedicated app-specific password just for this purpose
2. Accessing this over a secure VPN or private network
3. Running your own instance with the provided instructions
## Concept
AWS Nitro Enclaves provide isolated compute environments that enhance security through hardware-based attestation. When code runs in a Nitro Enclave, the platform generates cryptographic measurements of the code's identity and state. These measurements serve as a verifiable guarantee that the code has not been modified and is executing exactly as intended, protecting against tampering or unauthorized modifications. For more information, see this [blog post](https://blog.trailofbits.com/2024/02/16/a-few-notes-on-aws-nitro-enclaves-images-and-attestation/).
We use [Nitriding](https://github.com/brave/nitriding-daemon) to quickly deploy code in an AWS Nitro TEE.
## Verify the code attestation
To verify that the intended codebase is the one running in our TEE, you must reproduce running it in an AWS Nitro enclave yourself. Instructions to do so are below. Once you have it running, you can verify it using this repository as follows.
1. First build the code.
```sh
cd verifier
pnpm install && pnpm run build
```
2. Then run the verifier locally.
```
cd mcp/react-ts-webpack
pnpm i && pnpm run dev
```
3. Then open `http://localhost:8080/` in your browser. You will be prompted to add two fields
  (a) the PCR2 hash, which is a hash of the codebase
  (b) the Code attestation, which is signed by AWS
4. Click the "Verify Attestation" button
## Run your own instance in a TEE
You can reproduce running this server in a TEE as follows.
1. Use the AWS EC2 console to select a sufficiently large instance and be sure to enable Nitro.
2. Make sure that the ports needed by your application are open by checking the security group, in "security" tab of the instance in the ec2 console.
3. Clone this repo to your ec2 instance.
4. Run the setup script to download all necessary dependencies.
```bash
sudo /setup.sh
```
5. Allocate more memory for the enclave if necessary.
```bash
sudo nano /etc/nitro_enclaves/allocator.yaml
sudo systemctl restart nitro-enclaves-allocator.service
```
6. Run the enclave.
```bash
make
```
7. Run in production mode.
```bash
make run
```
## Use your MCP server
To actually use the MCP server, you will also need to run the gvproxy, as follows.
```bash
screen
./gvproxy.sh
```
Then you can `curl` the healthcheck endpoint to confirm that the MCP server is running in the enclave.
```bash
curl http://127.0.0.1:7047/
```
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/src/components/button.tsx:
--------------------------------------------------------------------------------
```typescript
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/src/app.css:
--------------------------------------------------------------------------------
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/postcss.config.js:
--------------------------------------------------------------------------------
```javascript
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}
```
--------------------------------------------------------------------------------
/verifier/test/worker.ts:
--------------------------------------------------------------------------------
```typescript
import * as Comlink from 'comlink';
import init from '../src/lib';
Comlink.expose({
  init,
});
```
--------------------------------------------------------------------------------
/verifier/test/utils.ts:
--------------------------------------------------------------------------------
```typescript
export function assert(expr: any, msg = 'unknown assertion error') {
  if (!Boolean(expr)) throw new Error(msg);
}
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/tailwind.config.js:
--------------------------------------------------------------------------------
```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
};
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/src/utils/misc.ts:
--------------------------------------------------------------------------------
```typescript
import { twMerge } from 'tailwind-merge';
import { clsx, type ClassValue } from 'clsx';
export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}
```
--------------------------------------------------------------------------------
/verifier/wasm/pkg/snippets/wasm-bindgen-futures-a509390b5b548b61/src/task/worker.js:
--------------------------------------------------------------------------------
```javascript
onmessage = function (ev) {
    let [ia, index, value] = ev.data;
    ia = new Int32Array(ia.buffer);
    let result = Atomics.wait(ia, index, value);
    postMessage(result);
};
```
--------------------------------------------------------------------------------
/verifier/tsconfig.compile.json:
--------------------------------------------------------------------------------
```json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "noEmit": false,
    "declaration": true,
    "declarationMap": true,
    "emitDeclarationOnly": true,
    "sourceMap": true,
    "outDir": "build"
  }
}
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/src/utils/worker.ts:
--------------------------------------------------------------------------------
```typescript
import * as Comlink from 'comlink';
import init, {
  verify_code_attestation,
  verify_attestation_signature,
} from 'tee-verifier-js';
Comlink.expose({
  init,
  verify_code_attestation,
  verify_attestation_signature,
});
```
--------------------------------------------------------------------------------
/verifier/serve.json:
--------------------------------------------------------------------------------
```json
{
  "headers": [{
    "source": "**/*",
    "headers": [
      {
        "key": "Cross-Origin-Embedder-Policy",
        "value": "require-corp"
      }, {
        "key": "Cross-Origin-Opener-Policy",
        "value": "same-origin"
      }
    ]
  }]
}
```
--------------------------------------------------------------------------------
/verifier/wasm/pkg/package.json:
--------------------------------------------------------------------------------
```json
{
  "name": "tlsn-wasm",
  "type": "module",
  "version": "0.1.0-alpha.6",
  "files": [
    "tlsn_wasm_bg.wasm",
    "tlsn_wasm.js",
    "tlsn_wasm.d.ts"
  ],
  "main": "tlsn_wasm.js",
  "types": "tlsn_wasm.d.ts",
  "sideEffects": [
    "./snippets/*"
  ]
}
```
--------------------------------------------------------------------------------
/gmail_mcp/pyproject.toml:
--------------------------------------------------------------------------------
```toml
[project]
name = "gmail"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
    "mcp[cli]>=1.6.0",
    "fastapi>=0.109.0",
    "uvicorn>=0.27.0",
    "starlette>=0.36.0",
    "py-dotenv>=0.1",
    "ruff>=0.11.3",
    "loguru>=0.7.3",
]
```
--------------------------------------------------------------------------------
/verifier/wasm/remote-attestation-verifier/package.json:
--------------------------------------------------------------------------------
```json
{
    "name": "remote-attestation-verifier-wasm",
    "type": "module",
    "version": "0.1.0-alpha.6",
    "files": [
      "remote-attestation-verifier_bg.wasm",
      "remote-attestation-verifier_bg.wasm.d.ts",
      "remote-attestation-verifier.js",
      "remote-attestation-verifier.d.ts"
    ],
    "main": "remote-attestation-verifier.js",
    "types": "remote-attestation-verifier.d.ts",
    "sideEffects": [
      "./snippets/*"
    ]
  }
```
--------------------------------------------------------------------------------
/run-enclave.sh:
--------------------------------------------------------------------------------
```bash
#!/bin/bash
image_eif="$1"
debug="$2"
if [ $image_eif == "" ]
then
	echo >&2 "Usage: $0 IMAGE_EIF"
	exit 1
fi
echo "image_eif: $image_eif"
if [ "$debug" = "--debug" ]
then
echo "🚀 Starting enclave in debug mode."
nitro-cli run-enclave \
	--cpu-count 2 \
	--memory 3000 \
	--enclave-cid 4 \
	--eif-path "$image_eif" \
	--debug-mode \
	--attach-console 
else
echo "🚀 Starting enclave in production mode."
nitro-cli run-enclave \
	--cpu-count 2 \
	--memory 3000 \
	--enclave-cid 4 \
	--eif-path "$image_eif" 
fi
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
  "compilerOptions": {
    "target": "es2015",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": false,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "noEmit": false,
    "jsx": "react",
    "types": ["jest", "node"]
  },
  "include": [
    "src/app.tsx"
  ]
}
```
--------------------------------------------------------------------------------
/verifier/wasm/remote-attestation-verifier/remote_attestation_verifier_bg.wasm.d.ts:
--------------------------------------------------------------------------------
```typescript
/* tslint:disable */
/* eslint-disable */
export const memory: WebAssembly.Memory;
export const verify_js: (a: number, b: number, c: number, d: number, e: any) => number;
export const ring_core_0_17_8_bn_mul_mont: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
export const __wbindgen_malloc: (a: number, b: number) => number;
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
export const __wbindgen_export_2: WebAssembly.Table;
export const __wbindgen_start: () => void;
```
--------------------------------------------------------------------------------
/gvproxy.sh:
--------------------------------------------------------------------------------
```bash
while true; do
    sudo pkill -f gvproxy
    sudo rm -rf /tmp/network.sock
    sudo /home/ec2-user/trusted-mcp-server/gvisor-tap-vsock/bin/gvproxy -listen vsock://:1024 -listen unix:///tmp/network.sock &
    sleep 2
    #open port for server
    sudo curl --unix-socket /tmp/network.sock \
        http:/unix/services/forwarder/expose \
        -X POST \
        -d '{"local":":7047","remote":"192.168.127.2:7047"}'
    # nitriding ports
    sudo curl --unix-socket /tmp/network.sock \
        http:/unix/services/forwarder/expose \
        -X POST \
        -d '{"local":":443","remote":"192.168.127.2:443"}'
    sleep 3000
done
```
--------------------------------------------------------------------------------
/verifier/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
  "compilerOptions": {
    "baseUrl": ".",
    "module": "esnext",
    "target": "es2015",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "sourceMap": true,
    "noEmit": true,
    "jsx": "react",
    "paths": {
      "crypto": ["node_modules/crypto-browserify"],
      "stream": ["node_modules/stream-browserify"],
      "vm": ["node_modules/vm-browserify"]
    },
    "types": ["jest", "node"]
  },
  "include": [
    "src"
  ]
}
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/src/app.tsx:
--------------------------------------------------------------------------------
```typescript
import React, { ReactElement } from 'react';
import { createRoot } from 'react-dom/client';
import './app.css';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { VerifyTee } from './components/verify-tee';
import { VerifyMCP } from './components/verify-mcp';
const container = document.getElementById('root');
const root = createRoot(container!);
root.render(<App />);
function App(): ReactElement {
  return (
    <Router>
      <div>
        <Routes>
          <Route path="/" element={<VerifyMCP />} />
          <Route path="/verify-tee" element={<VerifyTee />} />
          <Route path="/verify-mcp" element={<VerifyMCP />} />
        </Routes>
      </div>
    </Router>
  );
}
```
--------------------------------------------------------------------------------
/verifier/utils/build-tlsn-binaries.sh:
--------------------------------------------------------------------------------
```bash
#!/bin/bash
# Run tlsn Server fixture
# Set the directory to the location of the script
cd "$(dirname "$0")"
# Name of the directory where the repo will be cloned
REPO_DIR="tlsn"
# Check if the directory exists
if [ ! -d "$REPO_DIR" ]; then
    # Clone the repository if it does not exist
    git clone https://github.com/tlsnotary/tlsn.git "$REPO_DIR"
    cd "$REPO_DIR"
else
    # If the directory exists, just change to it
    cd "$REPO_DIR"
    # Fetch the latest changes in the repo without checkout
    git fetch
fi
# Checkout the specific tag
git checkout "v0.1.0-alpha.6"
for dir in "tlsn/tlsn-server-fixture/" "notary/server"; do
    # Change to the specific subdirectory
    cd ${dir}
    # Build the project
    cargo build --release
    cd -
done
```
--------------------------------------------------------------------------------
/verifier/utils/check-wasm.sh:
--------------------------------------------------------------------------------
```bash
#!/bin/bash
DIRECTORY="./wasm/prover/target"
FORCE_FLAG=0
# Check if --force or -f flag is passed
for arg in "$@"
do
    if [ "$arg" == "--force" ] || [ "$arg" == "-f" ]
    then
        FORCE_FLAG=1
        break
    fi
done
# If force flag is set, remove the directories/files and run the npm command
if [ $FORCE_FLAG -eq 1 ]
then
    echo "Force flag detected, removing directories and files."
    rm -rf ./wasm/prover/pkg
    rm -rf ./wasm/prover/target
    rm -f ./wasm/prover/Cargo.lock
    echo "Running npm run build:wasm"
    npm run build:wasm
# If the directory does not exist or is empty, run the npm command
elif [ ! -d "$DIRECTORY" ] || [ -z "$(ls -A $DIRECTORY)" ]
then
    echo "Running npm run build:wasm"
    npm run build:wasm
else
    echo "$DIRECTORY exists and is not empty."
fi
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
FROM golang:1.23 as builder
WORKDIR /    
# Clone the repository and build the stand-alone nitriding executable.
RUN git clone https://github.com/brave/nitriding-daemon.git
ARG TARGETARCH
RUN ARCH=${TARGETARCH} make -C nitriding-daemon/ nitriding
COPY start.sh  /bin/
COPY gmail_mcp /bin/mcp
RUN chown root:root /bin/mcp/server.py /bin/start.sh
RUN chmod 0755      /bin/mcp/server.py /bin/start.sh
FROM python:3.13-slim-bullseye
RUN apt-get update && apt-get install -y curl git procps && rm -rf /var/lib/apt/lists/*
# Copy all our files to the final image.
COPY --from=builder /nitriding-daemon/nitriding /bin/start.sh /bin/
COPY --from=builder /bin/mcp                    /bin/mcp
# Install PDM for Python package management
RUN pip install pdm
# Install Python dependencies
WORKDIR /bin/mcp
RUN pdm install --no-self --no-editable
CMD ["start.sh"]
```
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
```bash
#!/bin/sh
# Create log directory
mkdir -p /logs
# Start nitriding with logging
nitriding -fqdn example.com -ext-pub-port 443 -intport 8080 -appwebsrv http://127.0.0.1:7047 > /logs/nitriding.log 2>&1 &
NITRIDING_PID=$!
echo "[sh] Started nitriding with PID $NITRIDING_PID."
sleep 1
    
echo "🚀 starting python server"
cd /bin/mcp
pdm run server.py > /logs/server.log 2>&1 &
SERVER_PID=$!
echo "[sh] Python server started with PID $SERVER_PID."
# Function to check if a process is running
is_running() {
  ps -p $1 > /dev/null
  return $?
}
# Monitor processes and restart if needed
while true; do
  if ! is_running $NITRIDING_PID; then
    echo "[sh] Nitriding process died, restarting..."
    nitriding -fqdn example.com -ext-pub-port 443 -intport 8080 -appwebsrv http://127.0.0.1:7047 > /logs/nitriding.log 2>&1 &
    NITRIDING_PID=$!
    echo "[sh] Restarted nitriding with PID $NITRIDING_PID."
  fi
  
  if ! is_running $SERVER_PID; then
    echo "[sh] Python server died, restarting..."
    cd /bin/new
    pdm run server.py > /logs/server.log 2>&1 &
    SERVER_PID=$!
    echo "[sh] Restarted Python server with PID $SERVER_PID."
  fi
  
  sleep 10
done
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/package.json:
--------------------------------------------------------------------------------
```json
{
  "name": "react-ts-webpack",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.js",
  "scripts": {
    "dev": "webpack-dev-server --config webpack.js",
    "build": "webpack --config webpack.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "asn1js": "^3.0.5",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "comlink": "^4.4.1",
    "lucide-react": "^0.441.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-loader-spinner": "^6.1.6",
    "react-router-dom": "^6.26.2",
    "tailwind-merge": "^2.5.2",
    "tee-verifier-js": "../../"
  },
  "devDependencies": {
    "@types/react": "^18.0.26",
    "@types/react-dom": "^18.0.10",
    "autoprefixer": "^10.4.20",
    "babel-loader": "^9.1.3",
    "copy-webpack-plugin": "^11.0.0",
    "crypto-browserify": "^3.12.0",
    "css-loader": "^7.1.2",
    "html-webpack-plugin": "^5.5.0",
    "postcss": "^8.4.47",
    "postcss-loader": "^8.1.1",
    "source-map-loader": "^5.0.0",
    "stream-browserify": "^3.0.0",
    "style-loader": "^4.0.0",
    "tailwindcss": "^3.4.11",
    "ts-loader": "^9.4.2",
    "typescript": "^4.9.4",
    "vm-browserify": "^1.1.2",
    "webpack": "^5.75.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.11.1"
  }
}
```
--------------------------------------------------------------------------------
/verifier/wasm/pkg/snippets/wasm-bindgen-rayon-3e04391371ad0a8e/src/workerHelpers.worker.js:
--------------------------------------------------------------------------------
```javascript
/*
 * Copyright 2022 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Note: our JS should have been generated in
// `[out-dir]/snippets/wasm-bindgen-rayon-[hash]/workerHelpers.worker.js`,
// resolve the main module via `../../..`.
//
// This might need updating if the generated structure changes on wasm-bindgen
// side ever in the future, but works well with bundlers today. The whole
// point of this crate, after all, is to abstract away unstable features
// and temporary bugs so that you don't need to deal with them in your code.
import initWbg, { wbg_rayon_start_worker } from '../../../tlsn_wasm.js';
onmessage = async ({ data: { module, memory, receiver } }) => {
  await initWbg(module, memory);
  postMessage(true);
  wbg_rayon_start_worker(receiver);
};
```
--------------------------------------------------------------------------------
/verifier/wasm/remote-attestation-verifier/remote_attestation_verifier.d.ts:
--------------------------------------------------------------------------------
```typescript
/* tslint:disable */
/* eslint-disable */
export function verify_js(attestation_document: Uint8Array, nonce: Uint8Array, pcrs: Array<any>): boolean;
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export interface InitOutput {
  readonly memory: WebAssembly.Memory;
  readonly verify_js: (a: number, b: number, c: number, d: number, e: any) => number;
  readonly ring_core_0_17_8_bn_mul_mont: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
  readonly __wbindgen_malloc: (a: number, b: number) => number;
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
  readonly __wbindgen_export_2: WebAssembly.Table;
  readonly __wbindgen_start: () => void;
}
export type SyncInitInput = BufferSource | WebAssembly.Module;
/**
* Instantiates the given `module`, which can either be bytes or
* a precompiled `WebAssembly.Module`.
*
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
*
* @returns {InitOutput}
*/
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
```
--------------------------------------------------------------------------------
/verifier/webpack.build.config.js:
--------------------------------------------------------------------------------
```javascript
const webpack = require('webpack');
const path = require('path');
const isProd = process.env.NODE_ENV === 'production';
const envPlugin = new webpack.EnvironmentPlugin({
  NODE_ENV: 'development',
});
const rules = [
  {
    test: /\.tsx?$/,
    exclude: [/(node_modules|.webpack)/],
    rules: [
      {
        loader: 'ts-loader',
        options: {
          configFile: 'tsconfig.json',
          transpileOnly: true,
        },
      },
    ],
  },
  {
    test: /\.node$/,
    use: 'node-loader',
  },
];
var alias = {
  stream: require.resolve('stream-browserify'),
  // crypto: require.resolve('crypto-browserify'),
  // vm: require.resolve('vm-browserify'),
};
var fileExtensions = [
  'jpg',
  'jpeg',
  'png',
  'gif',
  'eot',
  'otf',
  'svg',
  'ttf',
  'woff',
  'woff2',
];
module.exports = [
  {
    mode: isProd ? 'production' : 'development',
    entry: {
      lib: path.join(__dirname, 'src', 'lib.ts'),
    },
    target: 'webworker',
    devtool: 'source-map',
    resolve: {
      extensions: ['.ts', '.js'],
    },
    node: {
      __dirname: true,
    },
    module: {
      rules: [...rules],
    },
    output: {
      publicPath: '',
      path: __dirname + '/build',
      filename: `[name].js`,
      libraryTarget: 'umd',
      globalObject: 'this',
      umdNamedDefine: true,
    },
    plugins: [
      envPlugin,
    ],
    resolve: {
      alias: alias,
      extensions: fileExtensions
        .map((extension) => '.' + extension)
        .concat(['.js', '.jsx', '.ts', '.tsx', '.css']),
      fallback: {
        stream: require.resolve('stream-browserify'),
      },
    }
  },
];
```
--------------------------------------------------------------------------------
/setup.sh:
--------------------------------------------------------------------------------
```bash
echo "📦 Setting up nitro instance"
# Amazon Linux 2023 uses dnf instead of amazon-linux-extras
echo "📦 Installing Nitro Enclave CLI"
sudo yum install -y aws-nitro-enclaves-cli aws-nitro-enclaves-cli-devel
# Create groups if they don't exist
echo "📦 Setting up user groups"
sudo groupadd -f ne
sudo groupadd -f docker
# Add user to groups
sudo usermod -aG ne ec2-user
sudo usermod -aG docker ec2-user
# Install Docker for AL2023
echo "📦 Installing Docker"
sudo yum install -y docker
sudo systemctl enable docker.service
sudo systemctl start docker.service
# Setup nitro enclaves service
echo "📦 Setting up Nitro Enclaves service"
sudo yum install -y aws-nitro-enclaves-cli-devel
sudo systemctl enable nitro-enclaves-allocator.service
sudo systemctl start nitro-enclaves-allocator.service
# Check if nitro-cli is in path, if not create symlink
if ! command -v nitro-cli &> /dev/null; then
    echo "📦 Setting up nitro-cli command"
    sudo ln -sf /usr/bin/nitro-cli-devel /usr/bin/nitro-cli
fi
# Verify nitro-cli installation
nitro-cli --version || echo "nitro-cli not available, may need to log out and back in"
echo "📦 Installing development tools"
sudo yum install -y make git
echo '📦 Installing Go'
GO_VERSION="1.23.8"  # Keep your original version
wget https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
rm go${GO_VERSION}.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
echo '📦 Verify Go version'
go version
echo '📦 Installing gvproxy'
git clone https://github.com/containers/gvisor-tap-vsock.git
cd gvisor-tap-vsock
make
echo "✅ Setup done, reboot with 'sudo reboot'"
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/src/components/shadcn.tsx:
--------------------------------------------------------------------------------
```typescript
import React from 'react';
import { cn } from '../utils/misc';
const Card = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn(
      'rounded-xl border bg-card text-card-foreground shadow',
      className,
    )}
    {...props}
  />
));
Card.displayName = 'Card';
const CardHeader = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn('flex flex-col space-y-1.5 p-6', className)}
    {...props}
  />
));
CardHeader.displayName = 'CardHeader';
const CardTitle = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
  <h3
    ref={ref}
    className={cn('font-semibold leading-none tracking-tight', className)}
    {...props}
  />
));
CardTitle.displayName = 'CardTitle';
const CardDescription = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
  <p
    ref={ref}
    className={cn('text-sm text-muted-foreground', className)}
    {...props}
  />
));
CardDescription.displayName = 'CardDescription';
const CardContent = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
));
CardContent.displayName = 'CardContent';
const CardFooter = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn('flex items-center p-6 pt-0', className)}
    {...props}
  />
));
CardFooter.displayName = 'CardFooter';
export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardDescription,
  CardContent,
};
```
--------------------------------------------------------------------------------
/verifier/test/assets/simple_proof_expected.json:
--------------------------------------------------------------------------------
```json
{
    "serverName": "example.com",
    "time": 1708595467,
    "sent": "GET / HTTP/1.1\r\nhost: example.com\r\naccept: */*\r\naccept-encoding: identity\r\nconnection: close\r\nuser-agent: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\r\n\r\n",
    "recv": "HTTP/1.1 200 OK\r\nAge: 519895\r\nCache-Control: max-age=604800\r\nContent-Type: text/html; charset=UTF-8\r\nDate: Thu, 22 Feb 2024 09:51:08 GMT\r\nEtag: \"3147526947+ident\"\r\nExpires: Thu, 29 Feb 2024 09:51:08 GMT\r\nLast-Modified: Thu, 17 Oct 2019 07:18:26 GMT\r\nServer: ECS (dce/26A0)\r\nVary: Accept-Encoding\r\nX-Cache: HIT\r\nContent-Length: 1256\r\nConnection: close\r\n\r\n<!doctype html>\n<html>\n<head>\n    <title>XXXXXXXXXXXXXX</title>\n\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <style type=\"text/css\">\n    body {\n        background-color: #f0f0f2;\n        margin: 0;\n        padding: 0;\n        font-family: -apple-system, system-ui, BlinkMacSystemFont, \"Segoe UI\", \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n        \n    }\n    div {\n        width: 600px;\n        margin: 5em auto;\n        padding: 2em;\n        background-color: #fdfdff;\n        border-radius: 0.5em;\n        box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n    }\n    a:link, a:visited {\n        color: #38488f;\n        text-decoration: none;\n    }\n    @media (max-width: 700px) {\n        div {\n            margin: 0 auto;\n            width: auto;\n        }\n    }\n    </style>    \n</head>\n\n<body>\n<div>\n    <h1>XXXXXXXXXXXXXX</h1>\n    <p>This domain is for use in illustrative examples in documents. You may use this\n    domain in literature without prior coordination or asking for permission.</p>\n    <p><a href=\"https://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n",
    "notaryUrl": "http://localhost"
}
```
--------------------------------------------------------------------------------
/verifier/wasm/pkg/snippets/wasm-bindgen-rayon-3e04391371ad0a8e/src/workerHelpers.js:
--------------------------------------------------------------------------------
```javascript
/*
 * Copyright 2022 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Note: this is never used, but necessary to prevent a bug in Firefox
// (https://bugzilla.mozilla.org/show_bug.cgi?id=1702191) where it collects
// Web Workers that have a shared WebAssembly memory with the main thread,
// but are not explicitly rooted via a `Worker` instance.
//
// By storing them in a variable, we can keep `Worker` objects around and
// prevent them from getting GC-d.
let _workers;
export async function startWorkers(module, memory, builder) {
  if (builder.numThreads() === 0) {
    throw new Error(`num_threads must be > 0.`);
  }
  const workerInit = {
    module,
    memory,
    receiver: builder.receiver()
  };
  _workers = await Promise.all(
    Array.from({ length: builder.numThreads() }, async () => {
      // Self-spawn into a new Worker.
      //
      // TODO: while `new URL('...', import.meta.url) becomes a semi-standard
      // way to get asset URLs relative to the module across various bundlers
      // and browser, ideally we should switch to `import.meta.resolve`
      // once it becomes a standard.
      const worker = new Worker(
        new URL('./workerHelpers.worker.js', import.meta.url),
        {
          type: 'module'
        }
      );
      worker.postMessage(workerInit);
      await new Promise(resolve =>
        worker.addEventListener('message', resolve, { once: true })
      );
      return worker;
    })
  );
  builder.build();
}
```
--------------------------------------------------------------------------------
/verifier/src/lib.ts:
--------------------------------------------------------------------------------
```typescript
import initWasm, {
  initThreadPool,
  init_logging,
  LoggingLevel,
  verify_attestation_document,
  verify_attestation_signature,
} from '../wasm/pkg/tlsn_wasm';
//import { verify_js } from '../wasm/remote-attestation-verifier/remote_attestation_verifier';
let LOGGING_LEVEL: LoggingLevel = 'Info';
function debug(...args: any[]) {
  if (['Debug', 'Trace'].includes(LOGGING_LEVEL)) {
    console.log('tlsn-js DEBUG', ...args);
  }
}
/**
 * Convert the PEM string represetation of a P256 public key to a hex string of its raw bytes
 * @param pemString - The PEM string to convert
 * @returns The raw hex string
 */
export function pemToRawHex(pemString: string) {
  const base64 = pemString
    .replace('-----BEGIN PUBLIC KEY-----', '')
    .replace('-----END PUBLIC KEY-----', '')
    .replace(/\s/g, '');
  return Buffer.from(base64, 'base64').toString('hex').slice(-130);
}
/**
 * It generates a random nonce of length 40 using hexadecimal characters.
 * This nonce is used to ensure the uniqueness of the attestation.
 * @returns {string} The generated nonce.
 */
export function generateNonce() {
  return Array.from({ length: 40 }, () =>
    Math.floor(Math.random() * 16).toString(16),
  ).join('');
}
export { verify_attestation_signature };
export async function verify_code_attestation(
  remote_attestation_base64: string,
  expected_nonce: string,
  expected_pcr: string,
  timestamp: number = Math.floor(Date.now() / 1000),
) {
  return await verify_attestation_document(
    remote_attestation_base64,
    expected_nonce,
    expected_pcr,
    BigInt(timestamp),
  );
}
export default async function init(config?: {
  loggingLevel?: LoggingLevel;
  hardwareConcurrency?: number;
}) {
  const {
    loggingLevel = 'Info',
    hardwareConcurrency = navigator.hardwareConcurrency,
  } = config || {};
  LOGGING_LEVEL = loggingLevel;
  const res = await initWasm();
  init_logging({
    level: loggingLevel,
    crate_filters: undefined,
    span_events: undefined,
  });
  // 6422528 ~= 6.12 mb
  debug('res.memory=', res.memory);
  debug('res.memory.buffer.length=', res.memory.buffer.byteLength);
  debug('DEBUG', 'initialize thread pool');
  await initThreadPool(hardwareConcurrency);
  debug('initialized thread pool');
  return true;
}
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/src/components/alert.tsx:
--------------------------------------------------------------------------------
```typescript
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '../utils/misc';
import { AlertCircle } from 'lucide-react';
const alertVariants = cva(
  'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7',
  {
    variants: {
      variant: {
        default: 'bg-background text-foreground',
        destructive:
          'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive',
      },
    },
    defaultVariants: {
      variant: 'default',
    },
  },
);
const Alert = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
  <div
    ref={ref}
    role="alert"
    className={cn(alertVariants({ variant }), className)}
    {...props}
  />
));
Alert.displayName = 'Alert';
const AlertTitle = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
  <h5
    ref={ref}
    className={cn('mb-1 font-medium leading-none tracking-tight', className)}
    {...props}
  />
));
AlertTitle.displayName = 'AlertTitle';
const AlertDescription = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn('text-sm [&_p]:leading-relaxed', className)}
    {...props}
  />
));
AlertDescription.displayName = 'AlertDescription';
export { Alert, AlertTitle, AlertDescription };
export function CodeHashCallout({ codeHash }: { codeHash: string }) {
  return (
    <Alert>
      <div className="flex items-center gap-2">
        <AlertCircle className="h-4 w-4" />
        <div className="group relative">
          <AlertTitle>Expected Code Hash</AlertTitle>
          <div className="invisible group-hover:visible absolute z-50 w-64 p-2 bg-gray-800 text-white text-sm rounded shadow-lg -top-12 left-0">
            This code hash represents the exact code running in the secure
            enclave
          </div>
        </div>
      </div>
      <AlertDescription>
        <code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold">
          {codeHash}
        </code>
      </AlertDescription>
    </Alert>
  );
}
```
--------------------------------------------------------------------------------
/verifier/webpack.web.dev.config.js:
--------------------------------------------------------------------------------
```javascript
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const isProd = process.env.NODE_ENV === 'production';
const envPlugin = new webpack.EnvironmentPlugin({
  NODE_ENV: 'development',
  LOCAL_NOTARY: true,
  LOCAL_WS: false,
  HEADLESS: false,
});
const rules = [
  {
    test: /\.node$/,
    use: 'node-loader',
  },
  {
    test: /\.tsx?$/,
    exclude: /(node_modules|.webpack)/,
    use: [
      {
        loader: 'ts-loader',
        options: {
          transpileOnly: true,
        },
      },
    ],
  },
];
const rendererRules = [];
module.exports = [
  {
    target: 'web',
    mode: isProd ? 'production' : 'development',
    entry: {
      'full-integration-swapi.spec': path.join(__dirname, 'test', 'specs', 'full-integration-swapi.spec.ts'),
      'simple-verify': path.join(__dirname, 'test', 'specs', 'simple-verify.spec.ts'),
    },
    output: {
      path: __dirname + '/test-build',
      publicPath: '/',
      filename: `[name].js`,
    },
    devtool: 'source-map',
    resolve: {
      extensions: ['.ts', '.tsx', '.js', '.jsx', '.png', '.svg'],
      // modules: [
      //   path.resolve('./node_modules'),
      //   path.resolve(__dirname, compilerOptions.baseUrl),
      // ],
      // fallback: {
      //   browserify: require.resolve('browserify'),
      //   stream: require.resolve('stream-browserify'),
      //   path: require.resolve('path-browserify'),
      //   crypto: require.resolve('crypto-browserify'),
      //   os: require.resolve('os-browserify/browser'),
      //   http: require.resolve('stream-http'),
      //   https: require.resolve('https-browserify'),
      //   assert: require.resolve('assert/'),
      //   events: require.resolve('events/'),
      //   'ansi-html-community': require.resolve('ansi-html-community'),
      //   'html-entities': require.resolve('html-entities'),
      //   constants: false,
      //   fs: false,
      // },
    },
    module: {
      rules: [...rules, ...rendererRules],
    },
    plugins: [
      envPlugin,
      new webpack.ProvidePlugin({
        Buffer: ['buffer', 'Buffer'],
      }),
      new webpack.ProvidePlugin({
        process: 'process',
      }),
      new HtmlWebpackPlugin({
        template: './test/test.ejs',
        filename: `index.html`,
        inject: true,
      }),
    ],
    stats: 'minimal',
    devServer: {
      historyApiFallback: true,
    },
  },
];
```
--------------------------------------------------------------------------------
/verifier/package.json:
--------------------------------------------------------------------------------
```json
{
  "name": "tee-verifier-js",
  "version": "v0.1.2",
  "description": "JS library for TEE applications",
  "main": "build/lib.js",
  "types": "build/lib.d.ts",
  "files": [
    "build/",
    "src/",
    "wasm/pkg/*",
    "readme.md"
  ],
  "scripts": {
    "build:test": "webpack --config webpack.web.dev.config.js",
    "serve:test": "serve  --config ../serve.json ./test-build -l 3001",
    "build:src": "webpack --config webpack.build.config.js",
    "build:types": "tsc --project tsconfig.compile.json",
    "build:tlsn-binaries": "sh utils/build-tlsn-binaries.sh",
    "build:lib": "NODE_ENV=production concurrently npm:build:src npm:build:types",
    "build": "npm run build:lib",
    "update:wasm": "sh utils/check-wasm.sh -f",
    "watch:dev": "webpack --config webpack.web.dev.config.js --watch",
    "predev": "sh utils/check-wasm.sh",
    "dev": "concurrently npm:watch:dev npm:serve:test",
    "lint:eslint": "eslint . --fix",
    "lint:tsc": "tsc --noEmit",
    "lint": "concurrently npm:lint:tsc npm:lint:eslint",
    "run:test": "TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' mocha -r ts-node/register 'test/testRunner.ts'",
    "test": "npm run build:tlsn-binaries && npm run build:test && npm run run:test",
    "test:only": "npm run build:test && npm run run:test"
  },
  "devDependencies": {
    "@types/expect": "^24.3.0",
    "@types/mocha": "^10.0.6",
    "@types/node": "^22.10.2",
    "@types/serve-handler": "^6.1.4",
    "browserify": "^17.0.0",
    "buffer": "^6.0.3",
    "comlink": "^4.4.1",
    "concurrently": "^5.1.0",
    "constants-browserify": "^1.0.0",
    "copy-webpack-plugin": "^5.0.5",
    "crypto-browserify": "^3.12.0",
    "eslint": "^8.57.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-prettier": "^5.0.0",
    "file-loader": "^5.0.2",
    "html-webpack-plugin": "~5.3.2",
    "https-browserify": "^1.0.0",
    "image-webpack-loader": "^6.0.0",
    "js-yaml": "^4.1.0",
    "mocha": "^10.2.0",
    "node-loader": "^0.6.0",
    "prettier": "^3.0.2",
    "process": "^0.11.10",
    "puppeteer": "^21.10.0",
    "serve": "14.2.1",
    "serve-handler": "^6.1.5",
    "stream-browserify": "^3.0.0",
    "ts-loader": "^6.2.1",
    "ts-mocha": "^10.0.0",
    "ts-node": "^10.9.2",
    "typescript": "^4.9.5",
    "typescript-eslint": "^7.4.0",
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.0",
    "webpack-dev-server": "^4.11.1",
    "webpack-node-externals": "^3.0.0"
  },
  "author": "",
  "license": "ISC",
  "engines": {
    "node": ">= 16.20.2"
  },
  "dependencies": {
    "@types/jest": "^29.5.14"
  }
}
```
--------------------------------------------------------------------------------
/verifier/test/specs/full-integration-swapi.spec.ts:
--------------------------------------------------------------------------------
```typescript
import {
  Prover as _Prover,
  NotaryServer,
  NotarizedSession as _NotarizedSession,
  TlsProof as _TlsProof,
} from '../../src/lib';
import { assert } from '../utils';
import * as Comlink from 'comlink';
const { init, Prover, NotarizedSession, TlsProof }: any = Comlink.wrap(
  // @ts-ignore
  new Worker(new URL('../worker.ts', import.meta.url)),
);
(async function () {
  try {
    await init({ loggingLevel: 'Debug' });
    // @ts-ignore
    console.log('test start');
    console.time('prove');
    const prover = (await new Prover({
      id: 'test',
      serverDns: 'swapi.dev',
    })) as _Prover;
    const notary = NotaryServer.from('http://localhost:7047');
    await prover.setup(await notary.sessionUrl());
    await prover.sendRequest('wss://notary.pse.dev/proxy?token=swapi.dev', {
      url: 'https://swapi.dev/api/people/1',
      headers: {
        'content-type': 'application/json',
        secret: 'test_secret',
      },
    });
    const transcript = await prover.transcript();
    console.log({ transcript });
    const commit = {
      sent: [
        ...Object.entries(transcript.ranges.sent.headers)
          .filter(([k]) => k !== 'secret')
          .map(([, v]) => v),
        transcript.ranges.sent.info,
        ...transcript.ranges.sent.lineBreaks,
      ],
      recv: [
        ...Object.entries(transcript.ranges.recv.headers).map(([, v]) => v),
        transcript.ranges.recv.info,
        ...transcript.ranges.recv.lineBreaks,
        transcript.ranges.recv.json!['name'],
        transcript.ranges.recv.json!['hair_color'],
        transcript.ranges.recv.json!['skin_color'],
      ],
    };
    console.log(commit);
    const sessionHex = await prover.notarize(commit);
    const session = (await new NotarizedSession(
      sessionHex,
    )) as _NotarizedSession;
    const proofHex = await session.proof(commit);
    console.log('proof:', proofHex);
    const proof = (await new TlsProof(proofHex)) as _TlsProof;
    console.timeEnd('prove');
    console.log('Proof: ', JSON.stringify(proof));
    console.time('verify');
    const result = await proof.verify({
      typ: 'P256',
      key: await notary.publicKey(),
    });
    console.timeEnd('verify');
    console.log(result);
    assert(result.sent.includes('host: swapi.dev'));
    assert(!result.sent.includes('secret: test_secret'));
    assert(result.recv.includes('"name":"Luke Skywalker"'));
    assert(result.recv.includes('"hair_color":"blond"'));
    assert(result.recv.includes('"skin_color":"fair"'));
    // @ts-ignore
    document.getElementById('full-integration-swapi').textContent = 'OK';
  } catch (err) {
    console.log('caught error from wasm');
    console.error(err);
    // @ts-ignore
    document.getElementById('full-integration-swapi').textContent = err.message;
  }
})();
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/webpack.js:
--------------------------------------------------------------------------------
```javascript
var webpack = require('webpack'),
  path = require('path'),
  CopyWebpackPlugin = require('copy-webpack-plugin'),
  HtmlWebpackPlugin = require('html-webpack-plugin');
const ASSET_PATH = process.env.ASSET_PATH || '/';
var alias = {};
var fileExtensions = [
  'jpg',
  'jpeg',
  'png',
  'gif',
  'eot',
  'otf',
  'svg',
  'ttf',
  'woff',
  'woff2',
];
var options = {
  ignoreWarnings: [
    /Circular dependency between chunks with runtime/,
    /ResizeObserver loop completed with undelivered notifications/,
  ],
  mode: 'development',
  entry: {
    app: path.join(__dirname, 'src', 'app.tsx'),
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'build'),
    clean: true,
    publicPath: ASSET_PATH,
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader'],
      },
      {
        test: new RegExp('.(' + fileExtensions.join('|') + ')$'),
        type: 'asset/resource',
        exclude: /node_modules/,
      },
      {
        test: /\.html$/,
        loader: 'html-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'source-map-loader',
          },
          {
            loader: require.resolve('ts-loader'),
          },
        ],
      },
      {
        test: /\.(js|jsx)$/,
        use: [
          {
            loader: 'source-map-loader',
          },
          {
            loader: require.resolve('babel-loader'),
          },
        ],
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    alias: alias,
    extensions: fileExtensions
      .map((extension) => '.' + extension)
      .concat(['.js', '.jsx', '.ts', '.tsx', '.css']),
    fallback: {
      crypto: require.resolve('crypto-browserify'),
      stream: require.resolve('stream-browserify'),
      vm: require.resolve('vm-browserify'),
    },
  },
  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'node_modules/tee-verifier-js/build',
          to: path.join(__dirname, 'build'),
          force: true,
        },
      ],
    }),
    new HtmlWebpackPlugin({
      template: path.join(__dirname, 'index.ejs'),
      filename: 'index.html',
      cache: false,
    }),
    new webpack.ProvidePlugin({
      process: 'process/browser',
    }),
    new webpack.ProvidePlugin({
      Buffer: ['buffer', 'Buffer'],
    }),
  ].filter(Boolean),
  // Required by wasm-bindgen-rayon, in order to use SharedArrayBuffer on the Web
  // Ref:
  //  - https://github.com/GoogleChromeLabs/wasm-bindgen-rayon#setting-up
  //  - https://web.dev/i18n/en/coop-coep/
  devServer: {
    headers: {
      'Cross-Origin-Embedder-Policy': 'require-corp',
      'Cross-Origin-Opener-Policy': 'same-origin',
    },
    historyApiFallback: true,
  },
};
module.exports = options;
```
--------------------------------------------------------------------------------
/verifier/wasm/pkg/tlsn_wasm_bg.wasm.d.ts:
--------------------------------------------------------------------------------
```typescript
/* tslint:disable */
/* eslint-disable */
export function init_logging(a: number): void;
export function verify_attestation_document(a: number, b: number, c: number, d: number, e: number, f: number, g: number): number;
export function verify_attestation_signature(a: number, b: number, c: number, d: number, e: number, f: number, g: number): number;
export function __wbg_signedsession_free(a: number, b: number): void;
export function signedsession_serialize(a: number, b: number): void;
export function signedsession_deserialize(a: number, b: number, c: number): void;
export function __wbg_prover_free(a: number, b: number): void;
export function prover_new(a: number): number;
export function prover_setup(a: number, b: number, c: number): number;
export function prover_send_request(a: number, b: number, c: number, d: number): number;
export function prover_notarize(a: number): number;
export function __wbg_verifier_free(a: number, b: number): void;
export function verifier_new(a: number): number;
export function verifier_connect(a: number, b: number, c: number): number;
export function verifier_verify(a: number): number;
export function __wbg_wbg_rayon_poolbuilder_free(a: number, b: number): void;
export function wbg_rayon_poolbuilder_numThreads(a: number): number;
export function wbg_rayon_poolbuilder_receiver(a: number): number;
export function wbg_rayon_poolbuilder_build(a: number): void;
export function initThreadPool(a: number): number;
export function wbg_rayon_start_worker(a: number): void;
export function ring_core_0_17_8_bn_mul_mont(a: number, b: number, c: number, d: number, e: number, f: number): void;
export const memory: WebAssembly.Memory;
export function __wbindgen_malloc(a: number, b: number): number;
export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
export const __wbindgen_export_3: WebAssembly.Table;
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h2e88a253da201d98(a: number, b: number, c: number): void;
export function _dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h33fdce4698adb901(a: number, b: number): void;
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h5c02e8a12b71e39b(a: number, b: number, c: number): void;
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h036d37595979be07(a: number, b: number, c: number): void;
export function __wbindgen_add_to_stack_pointer(a: number): number;
export function __wbindgen_free(a: number, b: number, c: number): void;
export function __wbindgen_exn_store(a: number): void;
export function wasm_bindgen__convert__closures__invoke2_mut__h134b145bfce1dc02(a: number, b: number, c: number, d: number): void;
export function __wbindgen_thread_destroy(a: number, b: number, c: number): void;
export function __wbindgen_start(a: number): void;
```
--------------------------------------------------------------------------------
/verifier/src/types.ts:
--------------------------------------------------------------------------------
```typescript
export type CommitData = {
  start: number;
  end: number;
};
export type ParsedTranscriptData = {
  all: CommitData;
  info: CommitData;
  headers: { [key: string]: CommitData };
  body?: CommitData;
  json?: { [path: string]: CommitData };
  lineBreaks: CommitData[];
};
export type ProofData = {
  time: number;
  server_dns: string;
  sent: string;
  sent_auth_ranges: { start: number; end: number }[];
  recv: string;
  recv_auth_ranges: { start: number; end: number }[];
};
export interface AttestationObject {
  version?: string;
  meta?: {
    notaryUrl: string;
    websocketProxyUrl: string;
  };
  signature: string; //signature of the application_data
  application_data: string; //hex string representation of bytes data
  application_data_decoded?: DecodedData;
  attributes: Attribute[]; //attributes extracted from the application_data
}
export interface RemoteAttestation {
  protected: string;
  payload: string;
  signature: string;
  certificate: string;
  payload_object: Payload;
}
export interface Payload {
  module_id: string;
  timestamp: number;
  digest: string;
  pcrs: Map<number, string>;
  certificate: Uint8Array;
  cabundle: Uint8Array[];
  public_key: Buffer;
  user_data: Uint8Array | null;
  nonce: string | null;
}
export type Attribute = {
  attribute_name: string;
  attribute_hex?: string;
  signature: string;
};
export type DecodedData = {
  hostname: string;
  request_url: string;
  request: string; //contain headers
  response_header: string;
  response_body: string;
  semaphore_identity_commitment: string;
};
export type NotaryRequest = {
  url: string;
  method: string;
  headers: Record<string, string>;
  body?: string;
  notaryUrl: string;
  websocketProxyUrl: string;
  timestamp: number;
};
type HttpMethod =
  | 'GET'
  | 'POST'
  | 'PUT'
  | 'DELETE'
  | 'HEAD'
  | 'OPTIONS'
  | 'PATCH';
type ResponseType = 'json' | 'text' | 'xml' | 'html';
export interface Provider {
  id: number;
  host: string; // e.g. api.x.com
  title: string;
  description: string;
  icon: string; //url to icon image
  urlRegex: string; // e.g. ^https://api\.x\.com/1\.1/account/settings\.json(\?.*)?$
  targetUrl: string; // URL to redirect user before notarization. e.g. https://www.x.com/home
  method: HttpMethod; // e.g. GET
  responseType: ResponseType;
  actionSelectors?: string[]; // url to redirect user before notarization. e.g. ["a[href^='/user/'][href$='/']"] or ["https://www.x.com/home"]
  preprocessor?: string; // Javascript function to process the response in a form that is more easy to evaluate. e.g. "function(data) { var result = ''; for (var key in data) { result += key + '=' + data[key] + '; '; } return JSON.parse(result); }"
  attributes?: string[]; // List of JMESPath expressions used to extract attributes from the provider's response.  e.g. ["screen_name"]
}
interface ExpectedPcrs {
  [key: string]: string; // Base64-encoded PCR value
}
export interface NotaryConfig {
  version: string;
  EXPECTED_PCRS: ExpectedPcrs;
  PROVIDERS: Provider[];
  [key: string]: any; // For additional properties
}
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/src/components/verify-tee.tsx:
--------------------------------------------------------------------------------
```typescript
import React, { ReactElement, useEffect, useState } from 'react';
import { CheckCircle, RefreshCw, XCircle } from 'lucide-react';
// import { Link } from 'react-router-dom';
import * as Comlink from 'comlink';
const { init, verify_code_attestation }: any = Comlink.wrap(
  new Worker(new URL('../utils/worker.ts', import.meta.url)),
);
const nonce = '0000000000000000000000000000000000000000';
const EXPECTED_PCRS = {
  '1': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
  '2': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
};
export function VerifyTee(): ReactElement {
  const [processingVerification, setProcessingVerification] = useState(false);
  const [resultVerify, setResultVerify] = useState<boolean | null>(null);
  const [codeAttestation, setCodeAttestation] = useState<null | string>('');
  const [error, setError] = useState<null | string>(null);
  const verify_attestation_document = async () => {
    if (!codeAttestation) {
      setError('Please enter a valid code attestation');
      return;
    }
    const codeAttestation_ = codeAttestation.replace(/\\n/g, '').trim();
    console.log('codeAttestation_', codeAttestation_);
    setProcessingVerification(true);
    try {
      const resultVerify = await verify_code_attestation(
        codeAttestation_,
        nonce,
        EXPECTED_PCRS,
        Math.floor(Date.now() / 1000),
      );
      setResultVerify(resultVerify);
    } catch (e) {
      setResultVerify(false);
      setError((e as Error).message);
    } finally {
      setProcessingVerification(false);
    }
  };
  useEffect(() => {
    const initialize = async () => {
      await init({ loggingLevel: 'Debug' });
    };
    initialize();
  }, []);
  return (
    <div>
      <div className="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
        <div className="flex justify-between items-center mb-4"></div>
        <div className="mt-2 h-30 overflow-y-auto border border-gray-200 rounded p-4 mb-4">
          <h2 className="text-l font-bold">
            {' '}
            1) Get your code attestation from enclave
          </h2>
          <h2 className="text-l font-bold">
            {' '}
            2) Paste code attestation in base64 format
          </h2>
          <textarea
            id="nonce"
            name="nonce"
            className="mt-1 block w-96 h-32 overflow-y-auto rounded-md border border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500 sm:text-sm"
            placeholder={codeAttestation ?? ''}
            onChange={(e) => setCodeAttestation(e.target.value)}
          />
        </div>
        <div className="p-8">
          <div className="flex justify-between items-center mb-4">
            <button
              onClick={verify_attestation_document}
              disabled={processingVerification}
              className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-opacity-50 disabled:opacity-50 disabled:cursor-not-allowed flex items-center"
            >
              {processingVerification ? (
                <>
                  <RefreshCw className="animate-spin -ml-1 mr-2 h-5 w-5" />
                  Verifying...
                </>
              ) : (
                <>
                  <CheckCircle className="mr-2 h-5 w-5" />
                  Verify Remote Attestation
                </>
              )}
            </button>
          </div>
          {resultVerify !== null && (
            <div
              className={`mt-4 p-4 rounded-md ${resultVerify ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`}
            >
              <div className="flex items-center">
                {resultVerify ? (
                  <CheckCircle className="h-5 w-5 mr-2" />
                ) : (
                  <XCircle className="h-5 w-5 mr-2" />
                )}
                <span className="font-medium">
                  {resultVerify
                    ? 'Remote attestation is valid'
                    : 'Remote attestation is invalid'}
                  {!resultVerify && <p>{error}</p>}
                </span>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}
```
--------------------------------------------------------------------------------
/verifier/test/testRunner.ts:
--------------------------------------------------------------------------------
```typescript
import puppeteer, { Browser, Page, PuppeteerLaunchOptions } from 'puppeteer';
import { describe, it, before, after } from 'mocha';
const assert = require('assert');
import { exec, ChildProcess } from 'node:child_process';
import * as fs from 'fs';
import path from 'path';
const yaml = require('js-yaml');
const timeout = 300000;
// puppeteer options
let opts: PuppeteerLaunchOptions = {
  headless: !!process.env.HEADLESS ? 'new' : false,
  slowMo: 100,
  timeout: timeout,
};
if (process.env.CHROME_PATH) {
  opts = {
    ...opts,
    executablePath: process.env.CHROME_PATH,
  };
}
let browser: Browser;
let page: Page;
let server: ChildProcess;
let tlsnServerFixture: ChildProcess;
const spawnTlsnServerFixture = () => {
  const tlsnServerFixturePath = './utils/tlsn/tlsn/tlsn-server-fixture/';
  // Spawn the server process
  // tlsnServerFixture = spawn(tlsnServerFixturePath, []);
  tlsnServerFixture = exec(`../target/release/main`, {
    cwd: tlsnServerFixturePath,
  });
  tlsnServerFixture.stdout?.on('data', (data) => {
    console.log(`Server: ${data}`);
  });
  tlsnServerFixture.stderr?.on('data', (data) => {
    console.error(`Server Error: ${data}`);
  });
};
let localNotaryServer: ChildProcess;
const spawnLocalNotaryServer = async () => {
  const localNotaryServerPath = './utils/tlsn/notary/server';
  localNotaryServer = exec(`../target/release/notary-server`, {
    cwd: localNotaryServerPath,
  });
  localNotaryServer.stdout?.on('data', (data) => {
    console.log(`Server: ${data}`);
  });
  localNotaryServer.stderr?.on('data', (data) => {
    console.error(`Server Error: ${data}`);
  });
  // wait for the notary server to be ready
  while (true) {
    try {
      const response = await fetch('http://127.0.0.1:7047/info');
      if (response.ok) {
        return;
      }
    } catch (error) {
      console.error('Waiting for local notary server...', error);
    }
    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
};
const configureNotarySerer = () => {
  try {
    const configPath = './utils/tlsn/notary/server/config/config.yaml';
    const fileContents = fs.readFileSync(configPath, 'utf8');
    const data = yaml.load(fileContents) as any;
    data.tls.enabled = false;
    data.server.host = '127.0.0.1';
    const newYaml = yaml.dump(data);
    fs.writeFileSync(configPath, newYaml, 'utf8');
    console.log('YAML file has been updated.');
  } catch (error) {
    console.error('Error reading or updating the YAML file:', error);
  }
};
// expose variables
before(async function () {
  server = exec('serve  --config ../serve.json ./test-build -l 3001');
  spawnTlsnServerFixture();
  configureNotarySerer();
  await spawnLocalNotaryServer();
  browser = await puppeteer.launch(opts);
  page = await browser.newPage();
  await page.goto('http://127.0.0.1:3001');
});
// close browser and reset global variables
after(async function () {
  console.log('Cleaning up:');
  try {
    tlsnServerFixture.kill();
    console.log('* Stopped TLSN Server Fixture ✅');
    localNotaryServer.kill();
    console.log('* Stopped Notary Server ✅');
    server.kill();
    console.log('* Stopped Test Web Server ✅');
    await page.close();
    await browser.close();
    const childProcess = browser.process();
    if (childProcess) {
      childProcess.kill(9);
    }
    console.log('* Closed browser ✅');
    process.exit(0);
  } catch (e) {
    console.error(e);
    process.exit(0);
  }
});
describe('tlsn-js test suite', function () {
  fs.readdirSync(path.join(__dirname, 'specs')).forEach((file) => {
    const [id] = file.split('.');
    it(`Test ID: ${id}`, async function () {
      const content = await check(id);
      assert(content === 'OK');
    });
  });
  // it('should prove and verify data from the local tlsn-server-fixture', async function () {
  //   const content = await check('full-integration-swapi');
  //   assert(content === 'OK');
  // });
  //
  // it('should verify', async function () {
  //   const content = await check('simple-verify');
  //   assert(content === 'OK');
  // });
});
async function check(testId: string): Promise<string> {
  const startTime = Date.now();
  const attemptFetchContent = async (): Promise<string> => {
    const content = await page.$eval(
      `#${testId}`,
      (el: any) => el.textContent || '',
    );
    if (content) return content;
    const elapsedTime = Date.now() - startTime;
    if (elapsedTime >= timeout) {
      throw new Error(
        `Timeout: Failed to retrieve content for '#${testId}' within ${timeout} ms.`,
      );
    }
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return attemptFetchContent();
  };
  return attemptFetchContent();
}
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/src/utils/requests.ts:
--------------------------------------------------------------------------------
```typescript
export interface RequestConfig {
  dns: string;
  url: string;
  method: string;
  headers: Record<string, string>;
  body?: any;
}
export const requests: Record<string, RequestConfig> = {
  dummy: {
    dns: 'dummyjson.com',
    url: 'https://dummyjson.com/products/1',
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
    },
    body: {
      hello: 'world',
      one: 1,
    },
  },
  swapi: {
    dns: 'swapi.dev',
    url: 'https://swapi.dev/api/people/1',
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
    },
    body: {
      hello: 'world',
      one: 1,
    },
  },
  twitter_profile: {
    dns: 'api.x.com',
    url: 'https://api.x.com/1.1/account/settings.json?include_ext_sharing_audiospaces_listening_data_with_followers=true&include_mention_filter=true&include_nsfw_user_flag=true&include_nsfw_admin_flag=true&include_ranked_timeline=true&include_alt_text_compose=true&ext=ssoConnections&include_country_code=true&include_ext_dm_nsfw_media_filter=true',
    method: 'GET',
    headers: {
      accept: '*/*',
      'accept-language':
        'en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-FR;q=0.6,zh-FR;q=0.5,zh;q=0.4,ar-FR;q=0.3,ar;q=0.2',
      authorization:
        'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA',
      cookie: '',
      dnt: '1',
      origin: 'https://x.com',
      priority: 'u=1, i',
      referer: 'https://x.com/',
      'sec-ch-ua':
        '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
      'sec-ch-ua-mobile': '?0',
      'sec-ch-ua-platform': '"Linux"',
      'sec-fetch-dest': 'empty',
      'sec-fetch-mode': 'cors',
      'sec-fetch-site': 'same-site',
      'user-agent':
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
      'x-client-transaction-id':
        '8bJcp2Wc6qg55CwNDbI1860M+C+hQh8BSxeSXvDbxWtpItZN8w589MSRhPa8j5Pe72PhnvPcCfkcKomHIJbPnoc1A2j48g',
      'x-csrf-token':
        '973e0be66e4b49591fb2475c8fb92ca0943b738898faa61276c31357ed3094b02eda226fb52e2166236683bcd046ce089be090e0ee3bf50f8272f12a3a47f8ec63199b024583f5e49a64a4f618ba28be',
      'x-twitter-active-user': 'yes',
      'x-twitter-auth-type': 'OAuth2Session',
      'x-twitter-client-language': 'en',
    },
  },
  twitter_login: {
    dns: 'api.x.com',
    url: 'https://api.x.com/1.1/help/settings.json?include_zero_rate=true&feature_set_token=c34254773736d750fd311352b336708862ecf6d6&settings_version=7fada87ecd68d5b2e904cbf5d7dad752',
    method: 'GET',
    headers: {
      accept: '*/*',
      'accept-language':
        'en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-FR;q=0.6,zh-FR;q=0.5,zh;q=0.4,ar-FR;q=0.3,ar;q=0.2',
      authorization:
        'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA',
      cookie: '',
      dnt: '1',
      origin: 'https://x.com',
      priority: 'u=1, i',
      referer: 'https://x.com/',
      'sec-ch-ua':
        '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
      'sec-ch-ua-mobile': '?0',
      'sec-ch-ua-platform': '"Linux"',
      'sec-fetch-dest': 'empty',
      'sec-fetch-mode': 'cors',
      'sec-fetch-site': 'same-site',
      'user-agent':
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
      'x-client-transaction-id':
        'zdz5xWCz5kUuKembq56wZi8a0WR8kEj4b975TbzD/BImMwSyw5xxDR0IQxEGh43hRA/eos8169huHc8zBNpDQ3qqZOZgzg',
      'x-csrf-token':
        '8ba1514a1bfc0528522f6bd38edbf9203af724e0dfb628c178c331ed82ee45d70e7bd0eeeff10bfed2d8c991fa0008702369febe127a2a08067ba59d56a6f5fabb108c97469c65587d360da1bc7382d3',
      'x-twitter-active-user': 'yes',
      'x-twitter-auth-type': 'OAuth2Session',
      'x-twitter-client-language': 'en',
    },
  },
  wise_balance: {
    dns: 'wise.com',
    url: 'https://wise.com/gateway/v4/profiles/39893034/balances?types=STANDARD,SAVINGS',
    method: 'GET',
    headers: {
      accept: 'application/json, text/plain, */*',
      'accept-language': 'en-US',
      cookie: '',
      dnt: '1',
      priority: 'u=1, i',
      referer: 'https://wise.com/home',
      'sec-ch-ua':
        '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
      'sec-ch-ua-arch': '"x86"',
      'sec-ch-ua-bitness': '"64"',
      'sec-ch-ua-mobile': '?0',
      'sec-ch-ua-model': '""',
      'sec-ch-ua-platform': '"Linux"',
      'sec-ch-ua-platform-version': '"6.8.0"',
      'sec-fetch-dest': 'empty',
      'sec-fetch-mode': 'cors',
      'sec-fetch-site': 'same-origin',
      'user-agent':
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
      'x-access-token': 'Tr4n5f3rw153',
      'x-visual-context': 'personal::light',
    },
  },
  ameli_login: {
    dns: 'assure.ameli.fr',
    url: 'https://assure.ameli.fr/PortailAS/appmanager/PortailAS/assure?_nfpb=true&_pageLabel=as_login_page&connexioncompte_2actionEvt=connecter',
    method: 'GET',
    headers: {
      Accept:
        'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
      'Accept-Language':
        'en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-FR;q=0.6,zh-FR;q=0.5,zh;q=0.4,ar-FR;q=0.3,ar;q=0.2',
      'Cache-Control': 'max-age=0',
      Connection: 'keep-alive',
      Cookie: '',
      DNT: '1',
      Referer: 'https://ameliconnect.ameli.fr/',
      'Sec-Fetch-Dest': 'document',
      'Sec-Fetch-Mode': 'navigate',
      'Sec-Fetch-Site': 'same-site',
      'Sec-Fetch-User': '?1',
      'Upgrade-Insecure-Requests': '1',
      'User-Agent':
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
      'sec-ch-ua':
        '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
      'sec-ch-ua-mobile': '?0',
      'sec-ch-ua-platform': '"Linux"',
    },
  },
  linkedin_feed: {
    dns: 'www.linkedin.com',
    url: 'https://www.linkedin.com/feed/',
    method: 'GET',
    headers: {
      Accept:
        'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
      'Accept-Language':
        'en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-FR;q=0.6,zh-FR;q=0.5,zh;q=0.4,ar-FR;q=0.3,ar;q=0.2',
      'Cache-Control': 'max-age=0',
      Connection: 'keep-alive',
      Cookie: '',
      DNT: '1',
      Priority: 'u=0, i',
      Referer: 'https://www.linkedin.com/checkpoint/lg/login-submit',
      'Sec-CH-UA':
        '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
      'Sec-CH-UA-Mobile': '?0',
      'Sec-CH-UA-Platform': '"Linux"',
      'Sec-Fetch-Dest': 'document',
      'Sec-Fetch-Mode': 'navigate',
      'Sec-Fetch-Site': 'same-origin',
      'Sec-Fetch-User': '?1',
      'Upgrade-Insecure-Requests': '1',
      'User-Agent':
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
    },
  },
  google_personal_info: {
    dns: 'myaccount.google.com',
    url: 'https://myaccount.google.com/personal-info?hl=fr&utm_source=OGB&utm_medium=act',
    method: 'GET',
    headers: {
      Accept:
        'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
      'Accept-Language': 'en-US,en;q=0.9',
      'Cache-Control': 'max-age=0',
      Cookie: '',
      DNT: '1',
      Priority: 'u=0, i',
      'Sec-CH-UA':
        '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"',
      'Sec-CH-UA-Arch': '"x86"',
      'Sec-CH-UA-Bitness': '"64"',
      'Sec-CH-UA-Form-Factors': '"Desktop"',
      'Sec-CH-UA-Full-Version': '"127.0.6533.119"',
      'Sec-CH-UA-Full-Version-List':
        '"Not)A;Brand";v="99.0.0.0", "Google Chrome";v="127.0.6533.119", "Chromium";v="127.0.6533.119"',
      'Sec-CH-UA-Mobile': '?0',
      'Sec-CH-UA-Model': '""',
      'Sec-CH-UA-Platform': '"Linux"',
      'Sec-CH-UA-Platform-Version': '"6.8.0"',
      'Sec-CH-UA-Wow64': '?0',
      'Sec-Fetch-Dest': 'document',
      'Sec-Fetch-Mode': 'navigate',
      'Sec-Fetch-Site': 'same-origin',
      'Sec-Fetch-User': '?1',
      'Upgrade-Insecure-Requests': '1',
      'User-Agent':
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36',
    },
  },
};
```
--------------------------------------------------------------------------------
/verifier/wasm/pkg/tlsn_wasm.d.ts:
--------------------------------------------------------------------------------
```typescript
/* tslint:disable */
/* eslint-disable */
/**
* Initializes logging.
* @param {LoggingConfig | undefined} [config]
*/
export function init_logging(config?: LoggingConfig): void;
/**
* @param {string} attestation_document
* @param {string} nonce_expected
* @param {string} pcr_expected
* @param {bigint} timestamp
* @returns {boolean}
*/
export function verify_attestation_document(attestation_document: string, nonce_expected: string, pcr_expected: string, timestamp: bigint): boolean;
/**
* @param {string} hex_application_data
* @param {string} hex_raw_signature
* @param {string} hex_raw_public_key
* @param {boolean} hash_appdata
* @returns {boolean}
*/
export function verify_attestation_signature(hex_application_data: string, hex_raw_signature: string, hex_raw_public_key: string, hash_appdata: boolean): boolean;
/**
* @param {number} num_threads
* @returns {Promise<any>}
*/
export function initThreadPool(num_threads: number): Promise<any>;
/**
* @param {number} receiver
*/
export function wbg_rayon_start_worker(receiver: number): void;
export interface AttestationDocument {
    protected: string | undefined;
    signature: string | undefined;
    payload: string | undefined;
    certificate: string | undefined;
}
export type Body = JsonValue;
export type Method = "GET" | "POST" | "PUT" | "DELETE";
export interface HttpRequest {
    uri: string;
    method: Method;
    headers: Map<string, number[]>;
    body: Body | undefined;
}
export interface HttpResponse {
    status: number;
    headers: [string, number[]][];
    body: string;
}
export interface Transcript {
    sent: number[];
    recv: number[];
}
export interface Commit {
    sent: { start: number; end: number }[];
    recv: { start: number; end: number }[];
}
export interface Reveal {
    sent: { start: number; end: number }[];
    recv: { start: number; end: number }[];
}
export interface VerifierData {
    server_dns: string;
    sent: number[];
    sent_auth_ranges: { start: number; end: number }[];
    received: number[];
    received_auth_ranges: { start: number; end: number }[];
}
export interface ProverConfig {
    id: string;
    server_dns: string;
    max_sent_data: number | undefined;
    max_recv_data: number | undefined;
}
export interface VerifierConfig {
    id: string;
    max_sent_data: number | undefined;
    max_received_data: number | undefined;
}
export interface CrateLogFilter {
    level: LoggingLevel;
    name: string;
}
export interface LoggingConfig {
    level: LoggingLevel | undefined;
    crate_filters: CrateLogFilter[] | undefined;
    span_events: SpanEvent[] | undefined;
}
export type SpanEvent = "New" | "Close" | "Active";
export type LoggingLevel = "Trace" | "Debug" | "Info" | "Warn" | "Error";
/**
*/
export class Prover {
  free(): void;
/**
* @param {ProverConfig} config
*/
  constructor(config: ProverConfig);
/**
* Set up the prover.
*
* This performs all Tee setup prior to establishing the connection to the
* application server.
* @param {string} verifier_url
* @returns {Promise<void>}
*/
  setup(verifier_url: string): Promise<void>;
/**
* Send the HTTP request to the server.
* @param {string} ws_proxy_url
* @param {HttpRequest} request
* @returns {Promise<HttpResponse>}
*/
  send_request(ws_proxy_url: string, request: HttpRequest): Promise<HttpResponse>;
/**
* Runs the notarization protocol.
* @returns {Promise<string>}
*/
  notarize(): Promise<string>;
}
/**
*/
export class SignedSession {
  free(): void;
/**
* Serializes to a byte array.
* @returns {Uint8Array}
*/
  serialize(): Uint8Array;
/**
* Deserializes from a byte array.
* @param {Uint8Array} bytes
* @returns {SignedSession}
*/
  static deserialize(bytes: Uint8Array): SignedSession;
}
/**
*/
export class Verifier {
  free(): void;
/**
* @param {VerifierConfig} config
*/
  constructor(config: VerifierConfig);
/**
* Connect to the prover.
* @param {string} prover_url
* @returns {Promise<void>}
*/
  connect(prover_url: string): Promise<void>;
/**
* Verifies the connection and finalizes the protocol.
* @returns {Promise<void>}
*/
  verify(): Promise<void>;
}
/**
*/
export class wbg_rayon_PoolBuilder {
  free(): void;
/**
* @returns {number}
*/
  numThreads(): number;
/**
* @returns {number}
*/
  receiver(): number;
/**
*/
  build(): void;
}
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export interface InitOutput {
  readonly init_logging: (a: number) => void;
  readonly verify_attestation_document: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
  readonly verify_attestation_signature: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => number;
  readonly __wbg_signedsession_free: (a: number, b: number) => void;
  readonly signedsession_serialize: (a: number, b: number) => void;
  readonly signedsession_deserialize: (a: number, b: number, c: number) => void;
  readonly __wbg_prover_free: (a: number, b: number) => void;
  readonly prover_new: (a: number) => number;
  readonly prover_setup: (a: number, b: number, c: number) => number;
  readonly prover_send_request: (a: number, b: number, c: number, d: number) => number;
  readonly prover_notarize: (a: number) => number;
  readonly __wbg_verifier_free: (a: number, b: number) => void;
  readonly verifier_new: (a: number) => number;
  readonly verifier_connect: (a: number, b: number, c: number) => number;
  readonly verifier_verify: (a: number) => number;
  readonly __wbg_wbg_rayon_poolbuilder_free: (a: number, b: number) => void;
  readonly wbg_rayon_poolbuilder_numThreads: (a: number) => number;
  readonly wbg_rayon_poolbuilder_receiver: (a: number) => number;
  readonly wbg_rayon_poolbuilder_build: (a: number) => void;
  readonly initThreadPool: (a: number) => number;
  readonly wbg_rayon_start_worker: (a: number) => void;
  readonly ring_core_0_17_8_bn_mul_mont: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
  readonly memory: WebAssembly.Memory;
  readonly __wbindgen_malloc: (a: number, b: number) => number;
  readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
  readonly __wbindgen_export_3: WebAssembly.Table;
  readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h2e88a253da201d98: (a: number, b: number, c: number) => void;
  readonly _dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h33fdce4698adb901: (a: number, b: number) => void;
  readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h5c02e8a12b71e39b: (a: number, b: number, c: number) => void;
  readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h036d37595979be07: (a: number, b: number, c: number) => void;
  readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
  readonly __wbindgen_free: (a: number, b: number, c: number) => void;
  readonly __wbindgen_exn_store: (a: number) => void;
  readonly wasm_bindgen__convert__closures__invoke2_mut__h134b145bfce1dc02: (a: number, b: number, c: number, d: number) => void;
  readonly __wbindgen_thread_destroy: (a?: number, b?: number, c?: number) => void;
  readonly __wbindgen_start: (a: number) => void;
}
export type SyncInitInput = BufferSource | WebAssembly.Module;
/**
* Instantiates the given `module`, which can either be bytes or
* a precompiled `WebAssembly.Module`.
*
* @param {{ module: SyncInitInput, memory?: WebAssembly.Memory, thread_stack_size?: number }} module - Passing `SyncInitInput` directly is deprecated.
* @param {WebAssembly.Memory} memory - Deprecated.
*
* @returns {InitOutput}
*/
export function initSync(module: { module: SyncInitInput, memory?: WebAssembly.Memory, thread_stack_size?: number } | SyncInitInput, memory?: WebAssembly.Memory): InitOutput;
/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {{ module_or_path: InitInput | Promise<InitInput>, memory?: WebAssembly.Memory, thread_stack_size?: number }} module_or_path - Passing `InitInput` directly is deprecated.
* @param {WebAssembly.Memory} memory - Deprecated.
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput>, memory?: WebAssembly.Memory, thread_stack_size?: number } | InitInput | Promise<InitInput>, memory?: WebAssembly.Memory): Promise<InitOutput>;
```
--------------------------------------------------------------------------------
/verifier/src/utils.ts:
--------------------------------------------------------------------------------
```typescript
import { ParsedTranscriptData } from './types';
export const SEMAPHORE_IDENTITY_HEADER = 'x-semaphore-identity';
type Stack =
  | {
      type: 'object';
      symbol: string;
      range: [number, number];
      id: number;
    }
  | {
      type: 'array';
      symbol: string;
      range: [number, number];
      data: string;
      id: number;
    }
  | {
      type: 'object_key';
      symbol: string;
      range: [number, number];
      data: string;
      path: string;
      id: number;
      objectId: number;
    }
  | {
      type: 'object_value';
      symbol: string;
      range: [number, number];
      data: string;
      id: number;
      keyId: number;
      objectId: number;
    }
  | {
      type: 'object_value_string';
      symbol: string;
      range: [number, number];
      data: string;
      path: string;
      id: number;
      objectId: number;
      valueId: number;
    }
  | {
      type: 'object_value_number';
      symbol: string;
      range: [number, number];
      data: string;
      path: string;
      id: number;
      objectId: number;
      valueId: number;
    };
type Commitment = {
  name?: string;
  path?: string;
  start: number;
  end: number;
};
export function processJSON(str: string): Commitment[] {
  const json = JSON.parse(str);
  expect(typeof json === 'object', 'json string must be an object');
  const stack: Stack[] = [];
  const values: Stack[] = [];
  const keys: string[] = [];
  let nonce = 0,
    keyId = 0;
  for (let i = 0; i < str.length; i++) {
    const char = str.charAt(i);
    let last = stack[stack.length - 1];
    if (last?.type === 'object_key') {
      if (char === '"') {
        last.range[1] = i;
        const key = stack.pop();
        expect(key!.type === 'object_key');
        if (key?.type === 'object_key') {
          keys.push(key.data);
          key.path = keys.join('.');
          values.push(key);
          keyId = last.id;
        }
      } else {
        last.data = last.data + char;
      }
      continue;
    }
    if (last?.type === 'object_value_string') {
      if (char === '"') {
        last.range[1] = i;
        values.push(stack.pop()!);
        const objectValue = stack.pop();
        expect(
          objectValue?.type === 'object_value',
          'expect stack to be object_value',
        );
        objectValue!.range[1] = i;
        values.push(objectValue!);
        keys.pop();
      } else {
        last.data = last.data + char;
      }
      continue;
    }
    if (last?.type === 'array') {
      if (char === ']') {
        last.range[1] = i;
        values.push(stack.pop()!);
      } else if (char === '[') {
        stack.push({
          symbol: '[',
          type: 'array',
          id: nonce++,
          range: [i, -1],
          data: '',
        });
      } else {
        last.data = last.data + char;
      }
      continue;
    }
    if (last?.type === 'object_value_number') {
      if (char === ',' || char === '}') {
        last.range[1] = i - 1;
        values.push(stack.pop()!);
        const objectValue = stack.pop();
        expect(
          objectValue?.type === 'object_value',
          'expect stack to be object_value',
        );
        objectValue!.range[1] = i - 1;
        values.push(objectValue!);
        last = stack[stack.length - 1];
      } else {
        last.data = last.data + char;
        continue;
      }
    }
    if (last?.type === 'object_value') {
      if (char === '}') {
        last.range[1] = i - 1;
        values.push(stack.pop()!);
        const object = stack.pop();
        expect(object?.type === 'object_value', 'expect stack to be object');
        object!.range[1] = i;
        values.push(object!);
        keys.pop();
      } else if (char === ',') {
        last.range[1] = i - 1;
        values.push(stack.pop()!);
        keys.pop();
      } else if (char === '{') {
        stack.push({
          symbol: '{',
          type: 'object',
          id: nonce++,
          range: [i, -1],
        });
      } else if (char === '[') {
        stack.push({
          symbol: '[',
          type: 'array',
          id: nonce++,
          range: [i, -1],
          data: '',
        });
      } else if (char === '"') {
        stack.push({
          symbol: '"',
          type: 'object_value_string',
          objectId: last.objectId,
          valueId: last.id,
          id: nonce++,
          data: '',
          range: [i, -1],
          path: '',
        });
      } else if (/^\d$/.test(char)) {
        stack.push({
          symbol: '"',
          type: 'object_value_number',
          objectId: last.objectId,
          valueId: last.id,
          id: nonce++,
          data: '',
          range: [i, -1],
          path: '',
        });
      }
      continue;
    }
    if (last?.type === 'object') {
      switch (char) {
        case '}':
          last.range[1] = i;
          values.push(stack.pop()!);
          continue;
        case '"':
          stack.push({
            symbol: '"',
            type: 'object_key',
            objectId: last.id,
            id: nonce++,
            data: '',
            range: [i, -1],
            path: '',
          });
          continue;
        case ':':
          stack.push({
            symbol: ':',
            type: 'object_value',
            objectId: last.id,
            keyId: keyId,
            id: nonce++,
            range: [i, -1],
            data: '',
          });
          continue;
        default:
          continue;
      }
    }
    switch (char) {
      case '{':
        stack.push({
          symbol: '{',
          type: 'object',
          id: nonce++,
          range: [i, -1],
        });
        break;
      case '[':
        stack.push({
          symbol: '[',
          type: 'array',
          id: nonce++,
          range: [i, -1],
          data: '',
        });
        break;
    }
  }
  expect(!stack.length, 'invalid stack length');
  const commitments: {
    [key: string]: Commitment;
  } = {};
  for (const value of values) {
    if (value.type === 'object_key') {
      commitments[value.id] = {
        ...(commitments[value.id] || {}),
        path: value.path,
        start: value.range[0],
      };
    } else if (value.type === 'object_value') {
      commitments[value.keyId] = {
        ...(commitments[value.keyId] || {}),
        end: value.range[1] + 1,
      };
    } else if (value.type === 'object') {
      commitments[value.id] = {
        start: value.range[0],
        end: value.range[1] + 1,
      };
    } else if (value.type === 'array') {
      commitments[value.id] = {
        start: value.range[0],
        end: value.range[1] + 1,
      };
    }
  }
  return Object.values(commitments).map(({ path, start, end }) => ({
    path,
    start,
    end,
  }));
}
export function processTranscript(transcript: string): ParsedTranscriptData {
  // const commitments: Commitment[] = [];
  const returnVal: ParsedTranscriptData = {
    all: {
      start: 0,
      end: transcript.length,
    },
    info: {
      start: 0,
      end: transcript.indexOf('\n') + 1,
    },
    headers: {},
    lineBreaks: [],
  };
  let text = '',
    ptr = -1,
    lineIndex = 0,
    isBody = false;
  for (let i = 0; i < transcript.length; i++) {
    const char = transcript.charAt(i);
    if (char === '\r') {
      _processEOL(text, i, lineIndex++);
      returnVal.lineBreaks.push({
        start: i,
        end: i + 1,
      });
      continue;
    }
    if (char === '\n') {
      text = '';
      ptr = -1;
      returnVal.lineBreaks.push({
        start: i,
        end: i + 1,
      });
      continue;
    }
    if (ptr === -1) {
      ptr = i;
    }
    text = text + char;
  }
  _processEOL(text, transcript.length - 1, lineIndex++);
  return returnVal;
  function _processEOL(txt: string, index: number, lineIndex: number) {
    try {
      if (!txt) return;
      if (!isNaN(Number(txt))) {
        isBody = true;
        return;
      }
      const json = JSON.parse(txt);
      returnVal.body = {
        start: ptr,
        end: index,
      };
      if (typeof json === 'object') {
        const jsonCommits = processJSON(txt);
        jsonCommits.forEach((commit) => {
          if (commit.path) {
            returnVal.json = returnVal.json || {};
            returnVal.json[commit.path] = {
              start: commit.start + ptr,
              end: commit.end + ptr,
            };
          }
        });
      }
    } catch (e) {
      const [name, value] = txt.split(': ');
      if (lineIndex === 0) {
        returnVal.info = {
          start: ptr,
          end: index,
        };
      } else if (!isBody && value) {
        returnVal.headers = returnVal.headers || {};
        returnVal.headers[name.toLowerCase()] = {
          start: ptr,
          end: index,
        };
      } else if (isBody) {
        returnVal.body = {
          // value: txt,
          start: ptr,
          end: index,
        };
      }
    }
  }
}
export function expect(cond: any, msg = 'invalid expression') {
  if (!cond) throw new Error(msg);
}
export function stringToBuffer(str: string): number[] {
  return Buffer.from(str).toJSON().data;
}
export function arrayToHex(uintArr: Uint8Array): string {
  return Buffer.from(uintArr).toString('hex');
}
export function headerToMap(headers: {
  [name: string]: string;
}): Map<string, number[]> {
  const headerMap: Map<string, number[]> = new Map();
  Object.entries(headers).forEach(([key, value]) => {
    headerMap.set(key, stringToBuffer(value));
  });
  return headerMap;
}
```
--------------------------------------------------------------------------------
/verifier/wasm/remote-attestation-verifier/remote_attestation_verifier.js:
--------------------------------------------------------------------------------
```javascript
let wasm;
function debugString(val) {
    // primitive types
    const type = typeof val;
    if (type == 'number' || type == 'boolean' || val == null) {
        return  `${val}`;
    }
    if (type == 'string') {
        return `"${val}"`;
    }
    if (type == 'symbol') {
        const description = val.description;
        if (description == null) {
            return 'Symbol';
        } else {
            return `Symbol(${description})`;
        }
    }
    if (type == 'function') {
        const name = val.name;
        if (typeof name == 'string' && name.length > 0) {
            return `Function(${name})`;
        } else {
            return 'Function';
        }
    }
    // objects
    if (Array.isArray(val)) {
        const length = val.length;
        let debug = '[';
        if (length > 0) {
            debug += debugString(val[0]);
        }
        for(let i = 1; i < length; i++) {
            debug += ', ' + debugString(val[i]);
        }
        debug += ']';
        return debug;
    }
    // Test for built-in
    const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
    let className;
    if (builtInMatches && builtInMatches.length > 1) {
        className = builtInMatches[1];
    } else {
        // Failed to match the standard '[object ClassName]'
        return toString.call(val);
    }
    if (className == 'Object') {
        // we're a user defined class or Object
        // JSON.stringify avoids problems with cycles, and is generally much
        // easier than looping through ownProperties of `val`.
        try {
            return 'Object(' + JSON.stringify(val) + ')';
        } catch (_) {
            return 'Object';
        }
    }
    // errors
    if (val instanceof Error) {
        return `${val.name}: ${val.message}\n${val.stack}`;
    }
    // TODO we could test for more things here, like `Set`s and `Map`s.
    return className;
}
let WASM_VECTOR_LEN = 0;
let cachedUint8ArrayMemory0 = null;
function getUint8ArrayMemory0() {
    if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
        cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
    }
    return cachedUint8ArrayMemory0;
}
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
    ? function (arg, view) {
    return cachedTextEncoder.encodeInto(arg, view);
}
    : function (arg, view) {
    const buf = cachedTextEncoder.encode(arg);
    view.set(buf);
    return {
        read: arg.length,
        written: buf.length
    };
});
function passStringToWasm0(arg, malloc, realloc) {
    if (realloc === undefined) {
        const buf = cachedTextEncoder.encode(arg);
        const ptr = malloc(buf.length, 1) >>> 0;
        getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
        WASM_VECTOR_LEN = buf.length;
        return ptr;
    }
    let len = arg.length;
    let ptr = malloc(len, 1) >>> 0;
    const mem = getUint8ArrayMemory0();
    let offset = 0;
    for (; offset < len; offset++) {
        const code = arg.charCodeAt(offset);
        if (code > 0x7F) break;
        mem[ptr + offset] = code;
    }
    if (offset !== len) {
        if (offset !== 0) {
            arg = arg.slice(offset);
        }
        ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
        const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
        const ret = encodeString(arg, view);
        offset += ret.written;
        ptr = realloc(ptr, len, offset, 1) >>> 0;
    }
    WASM_VECTOR_LEN = offset;
    return ptr;
}
let cachedDataViewMemory0 = null;
function getDataViewMemory0() {
    if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
        cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
    }
    return cachedDataViewMemory0;
}
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
function getStringFromWasm0(ptr, len) {
    ptr = ptr >>> 0;
    return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
}
function passArray8ToWasm0(arg, malloc) {
    const ptr = malloc(arg.length * 1, 1) >>> 0;
    getUint8ArrayMemory0().set(arg, ptr / 1);
    WASM_VECTOR_LEN = arg.length;
    return ptr;
}
/**
 * @param {Uint8Array} attestation_document
 * @param {Uint8Array} nonce
 * @param {Array<any>} pcrs
 * @returns {boolean}
 */
export function verify_js(attestation_document, nonce, pcrs) {
    const ptr0 = passArray8ToWasm0(attestation_document, wasm.__wbindgen_malloc);
    const len0 = WASM_VECTOR_LEN;
    const ptr1 = passArray8ToWasm0(nonce, wasm.__wbindgen_malloc);
    const len1 = WASM_VECTOR_LEN;
    const ret = wasm.verify_js(ptr0, len0, ptr1, len1, pcrs);
    return ret !== 0;
}
async function __wbg_load(module, imports) {
    if (typeof Response === 'function' && module instanceof Response) {
        if (typeof WebAssembly.instantiateStreaming === 'function') {
            try {
                return await WebAssembly.instantiateStreaming(module, imports);
            } catch (e) {
                if (module.headers.get('Content-Type') != 'application/wasm') {
                    console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
                } else {
                    throw e;
                }
            }
        }
        const bytes = await module.arrayBuffer();
        return await WebAssembly.instantiate(bytes, imports);
    } else {
        const instance = await WebAssembly.instantiate(module, imports);
        if (instance instanceof WebAssembly.Instance) {
            return { instance, module };
        } else {
            return instance;
        }
    }
}
function __wbg_get_imports() {
    const imports = {};
    imports.wbg = {};
    imports.wbg.__wbg_buffer_aa30bbb65cb44323 = function(arg0) {
        const ret = arg0.buffer;
        return ret;
    };
    imports.wbg.__wbg_get_01203e6a4116a116 = function(arg0, arg1) {
        const ret = arg0[arg1 >>> 0];
        return ret;
    };
    imports.wbg.__wbg_length_0a11127664108286 = function(arg0) {
        const ret = arg0.length;
        return ret;
    };
    imports.wbg.__wbg_length_9aaa2867670f533a = function(arg0) {
        const ret = arg0.length;
        return ret;
    };
    imports.wbg.__wbg_new_db41cf29086ce106 = function(arg0) {
        const ret = new Uint8Array(arg0);
        return ret;
    };
    imports.wbg.__wbg_set_e97d203fd145cdae = function(arg0, arg1, arg2) {
        arg0.set(arg1, arg2 >>> 0);
    };
    imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
        const ret = debugString(arg1);
        const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
        const len1 = WASM_VECTOR_LEN;
        getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
        getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
    };
    imports.wbg.__wbindgen_init_externref_table = function() {
        const table = wasm.__wbindgen_export_2;
        const offset = table.grow(4);
        table.set(0, undefined);
        table.set(offset + 0, undefined);
        table.set(offset + 1, null);
        table.set(offset + 2, true);
        table.set(offset + 3, false);
        ;
    };
    imports.wbg.__wbindgen_memory = function() {
        const ret = wasm.memory;
        return ret;
    };
    imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
        const ret = getStringFromWasm0(arg0, arg1);
        return ret;
    };
    imports.wbg.__wbindgen_throw = function(arg0, arg1) {
        throw new Error(getStringFromWasm0(arg0, arg1));
    };
    return imports;
}
function __wbg_init_memory(imports, memory) {
}
function __wbg_finalize_init(instance, module) {
    wasm = instance.exports;
    __wbg_init.__wbindgen_wasm_module = module;
    cachedDataViewMemory0 = null;
    cachedUint8ArrayMemory0 = null;
    wasm.__wbindgen_start();
    return wasm;
}
function initSync(module) {
    if (wasm !== undefined) return wasm;
    if (typeof module !== 'undefined') {
        if (Object.getPrototypeOf(module) === Object.prototype) {
            ({module} = module)
        } else {
            console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
        }
    }
    const imports = __wbg_get_imports();
    __wbg_init_memory(imports);
    if (!(module instanceof WebAssembly.Module)) {
        module = new WebAssembly.Module(module);
    }
    const instance = new WebAssembly.Instance(module, imports);
    return __wbg_finalize_init(instance, module);
}
async function __wbg_init(module_or_path) {
    if (wasm !== undefined) return wasm;
    if (typeof module_or_path !== 'undefined') {
        if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
            ({module_or_path} = module_or_path)
        } else {
            console.warn('using deprecated parameters for the initialization function; pass a single object instead')
        }
    }
    if (typeof module_or_path === 'undefined') {
        module_or_path = new URL('remote_attestation_verifier_bg.wasm', import.meta.url);
    }
    const imports = __wbg_get_imports();
    if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
        module_or_path = fetch(module_or_path);
    }
    __wbg_init_memory(imports);
    const { instance, module } = await __wbg_load(await module_or_path, imports);
    return __wbg_finalize_init(instance, module);
}
export { initSync };
export default __wbg_init;
```
--------------------------------------------------------------------------------
/verifier/mcp/react-ts-webpack/src/components/verify-mcp.tsx:
--------------------------------------------------------------------------------
```typescript
import React, { ReactElement, useEffect, useState, useRef } from 'react';
import { CheckCircle, RefreshCw, XCircle } from 'lucide-react';
import * as Comlink from 'comlink';
const { init, verify_code_attestation }: any = Comlink.wrap(
  new Worker(new URL('../utils/worker.ts', import.meta.url)),
);
// Utility function to convert hex to base64
const hexToBase64 = (hexString: string): string => {
  // Remove any spaces or 0x prefix
  const cleanedHexString = hexString.replace(/^0x|\s+/g, '');
  // Convert hex to bytes
  const bytes = new Uint8Array(cleanedHexString.match(/.{1,2}/g)?.map(byte => parseInt(byte, 16)) || []);
  // Convert bytes to base64
  return btoa(String.fromCharCode.apply(null, Array.from(bytes)));
};
export function VerifyMCP(): ReactElement {
  const [processingVerification, setProcessingVerification] = useState(false);
  const [resultVerify, setResultVerify] = useState<boolean | null>(null);
  const [expectedPcrHex, setExpectedPcrHex] = useState('5591c9354a1d280817ee1230a108c4c343d53fe66e2d8acc84340ddd4fe918d1144606c0cd9ea7f3a090ac3497c1854f');
  const [codeAttestation, setCodeAttestation] = useState<null | string>(
    'hEShATgioFkRYalpbW9kdWxlX2lkeCdpLTA4MGYzZGUxYmFjM2M3YzMxLWVuYzAxOTYwMmYyN2I4NzU5MjdmZGlnZXN0ZlNIQTM4NGl0aW1lc3RhbXAbAAABlgLzGwJkcGNyc7AAWDBFUEPrdk0/v62ciBOAcOJ77+ZUUBo1jIoxNBvwX/aF9jm8nDraLLLmGF4xvcexr5oBWDBLTVs2YbPvwSkgkAyA4Sbkzng8Ui3mwCoqW/evOiuTJ7hndvGI5L4cHEBKEp29pJMCWDBVkck1Sh0oCBfuEjChCMTDQ9U/5m4tisyENA3dT+kY0RRGBsDNnqfzoJCsNJfBhU8DWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEWDB0X/bIdR+LHVPHsIh19tt+vWoWkZIoGKNGU9EUNU66xAZtEE9AK3JevPb4nEF14cEFWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPWDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABrY2VydGlmaWNhdGVZAoAwggJ8MIICAaADAgECAhABlgLye4dZJwAAAABn8F8AMAoGCCqGSM49BAMDMIGOMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEPMA0GA1UECgwGQW1hem9uMQwwCgYDVQQLDANBV1MxOTA3BgNVBAMMMGktMDgwZjNkZTFiYWMzYzdjMzEudXMtZWFzdC0xLmF3cy5uaXRyby1lbmNsYXZlczAeFw0yNTA0MDQyMjM2NDVaFw0yNTA0MDUwMTM2NDhaMIGTMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEPMA0GA1UECgwGQW1hem9uMQwwCgYDVQQLDANBV1MxPjA8BgNVBAMMNWktMDgwZjNkZTFiYWMzYzdjMzEtZW5jMDE5NjAyZjI3Yjg3NTkyNy51cy1lYXN0LTEuYXdzMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEg0vrvuXWQITOvpdH6pQgDhbApXvJCYigoEVQbZF3IU0dUu3gOj9eLLjTijgRE6TGFVPdlJ8XofAoSp0QBWozTPmf5Ck4GQw0VtGwAFITMwsGv333jhq17IS3aoYlKmd1ox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDAKBggqhkjOPQQDAwNpADBmAjEAxvBELG/Zkg1YY43B/aj5G9H4mKwe8uqHyvmQoB7Rgku5on9+VYuL6msMTTbcS06pAjEAmjjP5VVyqHBeMDUvBBN1PImPitz474Dv//XAFmYiCcQdOFdK/2TxgxUXcXd5aVDEaGNhYnVuZGxlhFkCFTCCAhEwggGWoAMCAQICEQD5MXVoG5Cv4R1GzLTk5/hWMAoGCCqGSM49BAMDMEkxCzAJBgNVBAYTAlVTMQ8wDQYDVQQKDAZBbWF6b24xDDAKBgNVBAsMA0FXUzEbMBkGA1UEAwwSYXdzLm5pdHJvLWVuY2xhdmVzMB4XDTE5MTAyODEzMjgwNVoXDTQ5MTAyODE0MjgwNVowSTELMAkGA1UEBhMCVVMxDzANBgNVBAoMBkFtYXpvbjEMMAoGA1UECwwDQVdTMRswGQYDVQQDDBJhd3Mubml0cm8tZW5jbGF2ZXMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT8AlTrpgjB82hw4prakL5GODKSc26JS//2ctmJREtQUeU0pLH22+PAvFgaMrexdgcO3hLWmj/qIRtm51LPfdHdCV9vE3D0FwhD2dwQASHkz2MBKAlmRIfJeWKEME3FP/SjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJAltQ3ZBUfnlsOW+nKdz5mp30uWMA4GA1UdDwEB/wQEAwIBhjAKBggqhkjOPQQDAwNpADBmAjEAo38vkaHJvV7nuGJ8FpjSVQOOHwND+VtjqWKMPTmAlUWhHry/LjtV2K7ucbTD1q3zAjEAovObFgWycCil3UugabUBbmW0+96P4AYdalMZf5za9dlDvGH8K+sDy2/ujSMC89/2WQLDMIICvzCCAkWgAwIBAgIRAIe7NXr6E1k+s8LmGdT+JD4wCgYIKoZIzj0EAwMwSTELMAkGA1UEBhMCVVMxDzANBgNVBAoMBkFtYXpvbjEMMAoGA1UECwwDQVdTMRswGQYDVQQDDBJhd3Mubml0cm8tZW5jbGF2ZXMwHhcNMjUwNDAxMTczMjU1WhcNMjUwNDIxMTgzMjU1WjBkMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGQW1hem9uMQwwCgYDVQQLDANBV1MxNjA0BgNVBAMMLWQ5YmQxNjEwMzQ4OWJhNGQudXMtZWFzdC0xLmF3cy5uaXRyby1lbmNsYXZlczB2MBAGByqGSM49AgEGBSuBBAAiA2IABCnLwK6pMKzEH+P/0N0f3P47il7GPsfO2amw41XKwsjY6NBSD2crZ/65obLg3yPaRDwbwXMexnagOHUi2C3veR2b5nqo3ue4Sq0y+OGYwmq81gK3wAJIWPvVWvllDTMWS6OB1TCB0jASBgNVHRMBAf8ECDAGAQH/AgECMB8GA1UdIwQYMBaAFJAltQ3ZBUfnlsOW+nKdz5mp30uWMB0GA1UdDgQWBBSyW+U4AHixDbjWSVvglxlw7/dtWzAOBgNVHQ8BAf8EBAMCAYYwbAYDVR0fBGUwYzBhoF+gXYZbaHR0cDovL2F3cy1uaXRyby1lbmNsYXZlcy1jcmwuczMuYW1hem9uYXdzLmNvbS9jcmwvYWI0OTYwY2MtN2Q2My00MmJkLTllOWYtNTkzMzhjYjY3Zjg0LmNybDAKBggqhkjOPQQDAwNoADBlAjEA17DYTIQfzgOTOdZ6c0XFzfvqrAcOqoSoagX20Z/gMsDJcXtaDGBZ/bxycYyA+AdUAjB6jLvrNWGOW5rQj4sUvmoSjSD+VaK9Xio1RjLQD279PPNFfoOuPr4qjz3Cq/1Moi1ZAxkwggMVMIICm6ADAgECAhEAn4aXTaoDUy7H2ERvVoQ2/TAKBggqhkjOPQQDAzBkMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGQW1hem9uMQwwCgYDVQQLDANBV1MxNjA0BgNVBAMMLWQ5YmQxNjEwMzQ4OWJhNGQudXMtZWFzdC0xLmF3cy5uaXRyby1lbmNsYXZlczAeFw0yNTA0MDQxNjA3MzRaFw0yNTA0MTAxNjA3MzRaMIGJMTwwOgYDVQQDDDNhNWRiMzE2OGE2ZDdkNThlLnpvbmFsLnVzLWVhc3QtMS5hd3Mubml0cm8tZW5jbGF2ZXMxDDAKBgNVBAsMA0FXUzEPMA0GA1UECgwGQW1hem9uMQswCQYDVQQGEwJVUzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATDQ76SnGg0ijK9Is7zC46yIGNTKxI9or4VNXSkNymH31I7eRGpKTgSwvWUS8SnyabBcwGCMME+r2Yo8ijKLWTpZtkxAtd15kj6CojGamM7V/bl9bpICcdaoNSnoo4p2lSjgeowgecwEgYDVR0TAQH/BAgwBgEB/wIBATAfBgNVHSMEGDAWgBSyW+U4AHixDbjWSVvglxlw7/dtWzAdBgNVHQ4EFgQUWx2uRnLZKsbdpoQ77k3X13fNV4wwDgYDVR0PAQH/BAQDAgGGMIGABgNVHR8EeTB3MHWgc6Bxhm9odHRwOi8vY3JsLXVzLWVhc3QtMS1hd3Mtbml0cm8tZW5jbGF2ZXMuczMudXMtZWFzdC0xLmFtYXpvbmF3cy5jb20vY3JsLzVjOGY4MjcyLWQ2NDYtNDU0MS04ZjZhLTY0NjQ2NjcxNjVmZC5jcmwwCgYIKoZIzj0EAwMDaAAwZQIxAM1nCXl7SIl5sWDSgZIWArAkEhulhLMZVTpSVXXurjSsK3zkRW9ZhxgjpBBPjZDt9AIwRWigyqgSsb0TMZ39ha4n5h1Wv6o/QzCQ34drZP0IRe6O79YX+6MJ5fW4ktkVzk48WQLEMIICwDCCAkWgAwIBAgIVAJfmdmrchMPZjc9KEIk9a3QBtswBMAoGCCqGSM49BAMDMIGJMTwwOgYDVQQDDDNhNWRiMzE2OGE2ZDdkNThlLnpvbmFsLnVzLWVhc3QtMS5hd3Mubml0cm8tZW5jbGF2ZXMxDDAKBgNVBAsMA0FXUzEPMA0GA1UECgwGQW1hem9uMQswCQYDVQQGEwJVUzELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUwHhcNMjUwNDA0MjA1NjE3WhcNMjUwNDA1MjA1NjE3WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxDzANBgNVBAoMBkFtYXpvbjEMMAoGA1UECwwDQVdTMTkwNwYDVQQDDDBpLTA4MGYzZGUxYmFjM2M3YzMxLnVzLWVhc3QtMS5hd3Mubml0cm8tZW5jbGF2ZXMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQVZSNJbibU6gB79Apw+GUbSIban67m2Y2d0ULajgB2LBMGqbTboMgxuaJJ2pKe6vu1pUORRG7HwcUAJyyIAugq5NZr//W0kzZksFdjHUNY2OfgLZnyXVR4+uua5q/SE/qjZjBkMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBTsa5Pva5CFmj+1Knh89o2KZbEsNDAfBgNVHSMEGDAWgBRbHa5Gctkqxt2mhDvuTdfXd81XjDAKBggqhkjOPQQDAwNpADBmAjEAkYxiEIY4W6nyZjmtUVSVSHZqPm6OJgKm/wx0xMReTlQh+Lkw5+c7+iz1/mbfuw0TAjEAlJKa+guBi2MrLFk6i9vIllxeA/quglrHXD5dR4YnW0EvFONlUsrt91rkpV/d1E6nanB1YmxpY19rZXlFZHVtbXlpdXNlcl9kYXRhWEQSIMV1oXFG2VsTUlEZAqS9q5QVuVqFhzmbql5n4DOCkU9REiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGVub25jZVQAAAAAAAAAAAAAAAAAAAAAAAAAAFhgVu8yhDO+KaVJB/inIwbUCDxNR/iYgyHYvR60nw67ZKmCJpxpMdo2VYvEIhG34QQ8gxmQhoIMcPdzR/+MXwq+PaJ5FbCzJaCDuVsvLCpTl0W7+5I5An20KaseBBPFElcg'
  );
  const [error, setError] = useState<null | string>(null);
  const textareaRef = useRef<HTMLTextAreaElement>(null);
  // Function to auto-resize textarea
  const autoResizeTextarea = () => {
    const textarea = textareaRef.current;
    if (textarea) {
      textarea.style.height = 'auto';
      textarea.style.height = `${textarea.scrollHeight}px`;
    }
  };
  // Call resize on content change
  useEffect(() => {
    autoResizeTextarea();
  }, [codeAttestation]);
  const verify_attestation_document = async () => {
    if (!codeAttestation) {
      setError('Please enter a valid code attestation');
      return;
    }
    const codeAttestation_ = codeAttestation.replace(/\\n/g, '').trim();
    const nonce = '0000000000000000000000000000000000000000';
    
    // Convert hex PCR to base64 for verification
    let expectedPcrBase64;
    try {
      expectedPcrBase64 = hexToBase64(expectedPcrHex);
    } catch (e) {
      setError('Invalid hex format for PCR');
      return;
    }
    setProcessingVerification(true);
    try {
      const resultVerify = await verify_code_attestation(
        codeAttestation_,
        nonce,
        expectedPcrBase64,
        Math.floor(Date.now() / 1000),
      );
      setResultVerify(resultVerify);
    } catch (e) {
      console.log('error', e);
      setResultVerify(false);
      setError((e as Error).message);
    } finally {
      setProcessingVerification(false);
    }
  };
  useEffect(() => {
    const initialize = async () => {
      await init({ loggingLevel: 'Debug' });
    };
    initialize();
  }, []);
  return (
    <div>
      <div className="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
        <div className="flex justify-between items-center mb-4"></div>
        <div className="mt-2 h-30 overflow-y-auto border border-gray-200 rounded p-4 mb-4">
          <h1 className="text-2xl font-bold text-center my-4">
            Verify MCP Server
          </h1>
          <div className="mb-4">
            <h2 className="text-l font-bold">Expected PCR2 Hash</h2>
            <pre className="bg-gray-100 p-2 rounded text-xs mt-2 mb-2 overflow-x-auto">
              <code>$ nitro-cli describe-enclaves | jq -r '.[0].Measurements.PCR2'</code>
            </pre>
            <textarea
              id="expectedPcr"
              name="expectedPcr"
              className="mt-1 block w-full rounded-md border border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500 sm:text-sm font-mono"
              placeholder="Expected PCR value in hex format (e.g., 559249354a1d2808...)"
              value={expectedPcrHex}
              onChange={(e) => setExpectedPcrHex(e.target.value)}
            />
          </div>
          <h2 className="text-l font-bold">Code attestation</h2>
          <pre className="bg-gray-100 p-2 rounded text-xs mt-2 mb-2 overflow-x-auto">
            <code>$ curl -k https://127.0.0.1:443/enclave/attestation?nonce=0000000000000000000000000000000000000000</code>
          </pre>
          <textarea
            ref={textareaRef}
            id="attestation"
            name="attestation"
            className="mt-1 block w-full min-h-64 overflow-y-auto rounded-md border border-gray-300 shadow-sm focus:border-green-500 focus:ring-green-500 sm:text-sm font-mono"
            placeholder={
              codeAttestation ||
              'Paste the attestation object from the MCP'
            }
            onChange={(e) => {
              setCodeAttestation(e.target.value);
              // Call the resize function after setting the new value
              setTimeout(autoResizeTextarea, 0);
            }}
          />
        </div>
        <div className="p-8">
          <div className="flex justify-between items-center mb-4">
            <button
              onClick={verify_attestation_document}
              disabled={processingVerification}
              className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-opacity-50 disabled:opacity-50 disabled:cursor-not-allowed flex items-center mx-auto"
            >
              {processingVerification ? (
                <>
                  <RefreshCw className="animate-spin -ml-1 mr-2 h-5 w-5" />
                  Verifying...
                </>
              ) : (
                <>
                  <CheckCircle className="mr-2 h-5 w-5" />
                  Verify Attestation
                </>
              )}
            </button>
          </div>
          {resultVerify !== null && (
            <div
              className={`mb-4 mt-4 p-4 rounded-md ${resultVerify ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`}
            >
              <div className="flex items-center ">
                {!resultVerify && <XCircle className="h-5 w-5 mr-2" />}
                {resultVerify ? (
                  <div>
                    ✅ Code hash matches open-source implementation <br />✅
                    Attestation verified against Amazon's root-of-trust <br />✅
                    Hardware instance authenticity confirmed
                  </div>
                ) : (
                  <span className="font-medium">
                    Remote attestation is invalid
                  </span>
                )}
                {!resultVerify && <p>{error}</p>}
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}
```
--------------------------------------------------------------------------------
/verifier/test/specs/simple-verify.spec.ts:
--------------------------------------------------------------------------------
```typescript
import { TlsProof as _TlsProof } from '../../src/lib';
import { assert } from '../utils';
import * as Comlink from 'comlink';
const { init, TlsProof }: any = Comlink.wrap(
  // @ts-ignore
  new Worker(new URL('../worker.ts', import.meta.url)),
);
(async function verify_simple() {
  try {
    await init({ loggingLevel: 'Debug' });
    console.time('verify');
    const proofHex = `ec5957996676ebb7a9fae815b95fc1810f51da828330451a98ac42d0fba400a04fdceca500ec864f455f80059eb489f39479fc6627a07c6b878fa12a4d0b81a18800000000000000d50300000000000083d0b96600000000000000004100000000000000048b68632f53328caddef6fd3bd7fb941e86817baa8c253b208c244842ecabef403f0ecfff9b8677be3126b746b74e87fcfd07c7dcde3630c184ddf2b6f8a751b4cfd0297a1196dab752fe9c8a178292c689e53b8c1d5705c117451402d1f7d90901000000007ea696ce46ee2a8d47d740d0bed704382a09b947cbd195467516bcc98823c33d2fb1f3a987a2cf7f26f675a14da6632c08797a1f62cecc68037dbcc7e442154c00000000090000000000000073776170692e646576f55ad8214e395cff68b4473e01edd56f7ae8fb13a82bb7a14dd15209ea92cc820200000000000000e504000000000000308204e1308203c9a0030201020212040e6bddc9c867581bd1fe861e51effc6741300d06092a864886f70d01010b05003033310b300906035504061302555331163014060355040a130d4c6574277320456e6372797074310c300a06035504031303523130301e170d3234303732353138313030325a170d3234313032333138313030315a3014311230100603550403130973776170692e64657630820122300d06092a864886f70d01010105000382010f003082010a0282010100ab14ab8a1c5d57666595d89614a985463bcb64057e324b5f747da1fc3ad527b0c3582849971c07e8815bed09414e5e18bde5e353bf8d9df4662c858dc39614cd2c2f688c413ebe38738db4e812b9505149e777ff2f694a3318ffe04f62f7052c7442b73515f2058b9c6db5f94daea900eba8c1c8dd0db05f7467996f73cb7ffc7ce930e0f2e95d6c420dba923412065003ea8ba716a0232f2dfd16368f6274fb23663b7561b8f38d3b061c49e5b5d35bba2b03f428af683074b38b33d6fcde4a9722a800ad4d4fba2cdf22b0b1444dc744c322c597fee0edc698fdd3c7cfaaa8fa63c23529b6995863552636dbadb358281bdf16ab7c5236a4eeb9ae2352aee10203010001a382020c30820208300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b06010505070302300c0603551d130101ff04023000301d0603551d0e041604145b2712a1f0716bc6a81589f49af7ba123c7bfdac301f0603551d23041830168014bbbcc347a5e4bca9c6c3a4720c108da235e1c8e8305706082b06010505070101044b3049302206082b060105050730018616687474703a2f2f7231302e6f2e6c656e63722e6f7267302306082b060105050730028617687474703a2f2f7231302e692e6c656e63722e6f72672f30140603551d11040d300b820973776170692e64657630130603551d20040c300a3008060667810c01020130820103060a2b06010401d6790204020481f40481f100ef0075003f174b4fd7224758941d651c84be0d12ed90377f1f856aebc1bf2885ecf8646e00000190eb4ce2b30000040300463044022002dc6df6010b672341044a043bd0f226a8e7d82a91ec625b538caecc86e7a128022011f0a85da14142642ead5e2a9d48fd44914940df2044942500097f313693a1e100760048b0e36bdaa647340fe56a02fa9d30eb1c5201cb56dd2c81d9bbbfab39d8847300000190eb4ce2aa00000403004730450220720519eda2def160c991ea840ccc032f42f7f1ff32a200d9a2699c62366b7fbb022100de0aa1bff38efdef5c4e4ee8ae42eb02831a95d8c5a5df32a94c01672efc70e7300d06092a864886f70d01010b050003820101001386e3aa6c1e17247420870f89615f903c5020df62fa842e24a950516e6c7c82002befe448326db8b1baecdd7484c41b5256fc64b0f727e96e57189e45dbb6b7d9b8fcb4600154b1620738781643129a763c4761bb5e8781a11157654fc3fafad14c654e5dc8c3b131e61023ea03b9285bc651ff209f31a8d617b42524644731233ed88a5722a4ed5406ff6c877c20ed55fce6eb377d3d4018dbba99ba680932772cf1bb07d7e8e48cb1b210d1780ce168738e4d78bcf1da5a619c07d645629b5b0a3d0dca55aef13f24dc592266cf39fb1cdccca25a93f270c56c581776ba87a52cc7291496cd5feac433dffb986c7484643233232e11fa893634d0c7fc3bd9090500000000000030820505308202eda00302010202104ba85293f79a2fa273064ba8048d75d0300d06092a864886f70d01010b0500304f310b300906035504061302555331293027060355040a1320496e7465726e65742053656375726974792052657365617263682047726f7570311530130603550403130c4953524720526f6f74205831301e170d3234303331333030303030305a170d3237303331323233353935395a3033310b300906035504061302555331163014060355040a130d4c6574277320456e6372797074310c300a0603550403130352313030820122300d06092a864886f70d01010105000382010f003082010a0282010100cf57e5e6c45412edb447fec92758764650288c1d3e88df059dd5b51829bdddb55abffaf6cea3beaf00214b625a5a3c012fc55803f689ff8e1143ebc1b5e01407968f6f1fd7e7ba8139097565b7c2af185b372628e7a3f4072b6d1affab58bc95ae40ffe9cb57c4b55b7f780d1861bc17e754c6bb4991cd6e18d18085eea66536bc74eabc504ceafc21f338169394bab0d36b3806cd16127aca5275c8ad76b2c29c5d98455c6f617bc62dee3c13528601d957e6381cdf8db51f92919ae74a1ccc45a87255f0b0e6a307ecfda71b669e3f488b71847158c93afaef5ef25b442b3c74e78fb247c1076acd9ab70d96f712812651540aec61f6f7f5e2f28ac8950d8d0203010001a381f83081f5300e0603551d0f0101ff040403020186301d0603551d250416301406082b0601050507030206082b0601050507030130120603551d130101ff040830060101ff020100301d0603551d0e04160414bbbcc347a5e4bca9c6c3a4720c108da235e1c8e8301f0603551d2304183016801479b459e67bb6e5e40173800888c81a58f6e99b6e303206082b0601050507010104263024302206082b060105050730028616687474703a2f2f78312e692e6c656e63722e6f72672f30130603551d20040c300a3008060667810c01020130270603551d1f0420301e301ca01aa0188616687474703a2f2f78312e632e6c656e63722e6f72672f300d06092a864886f70d01010b0500038202010092b1e74137eb799d81e6cde225e13a20e9904495a3815ccfc35dfdbda070d5b19628220bd2f228cf0ce7d4e6438c24221dc14292d109af9f4bf4c8704f2016b15add01f61ff81f616b1427b0728d63aeeee2ce4bcf37ddbba3d4cde7ad50adbdbfe3ec3e6236709931a7e88dddea62e212aef59cd43d2c0caad09c79beea3d5c446e9631635a7dd67e4f24a04b057f5e6fd2d4ea5f334b13d657b6cade51b85da3098274fdc7789eb3b9ac16da4a2b96c3b68b628ff97419a29e03dee96f9bb00fd2a05af6855cc204b7c8d54e32c4bf045dbc29f6f7818f0c5d3c53c940908bfbb60865b9a421d509e51384843782ce1028fc76c206257a46524dda5372a4273f6270acbe694800fb670fdb5ba1e8d703212dd7c9f69942398343df770a1208f125d6ba9419541888a5c58ee11a9993796bec1cf93140b0cc3200df9f5ee7b492ab9082918d0de01e95ba593b2e4b5fc2b74635523906c0bdaaac52c122a0449799f70ca021a7a16c714716170168c0caa62665047cb3aec9e79455c26f9b3c1ca9f92ec5201af076e0beec18d64fd825fb7611e8bfe6210fe8e8ccb5b6a7d5b8f79f41cf6122466a83b668972e7cea4e95db23eb2ec82b2884a460e949f4442e3bf9ca625701e25d9016f9c9fc7a23488ea6d58172f128fa5dcefbed4e738f942ed241949899dba7af705ff5befb0220bf66276cb4adfa75120b2b3ece039e000000000000000000450000000000000003001741048b68632f53328caddef6fd3bd7fb941e86817baa8c253b208c244842ecabef403f0ecfff9b8677be3126b746b74e87fcfd07c7dcde3630c184ddf2b6f8a751b40600000000010000000000009d5146d5eacf609424a546a7b5532f54b60477c42ef1d511f2043da4d477dc19cc4dffb952bcc2a7754fbd0d58e2bc661a22cb037884bb796a59f7a6174e9f6c83dd800348dc6003135b40a2d29f63e9dc3b3f58092d12e51d831d0ae6703d8da918608c9e16516a7d702124ac898960cb7715d69219913d2d67284075a3480516c92fe6f28755428edf2fa92b0ed7a85631c346fc967e684ae417c41b70eb4971fe48b9139d00f0f7039ae7dd2724287c977ab906866e8cd59e6d0a08b33480736174d338b7773556404b97233dbec2bb0fd4cf188d28803d1d6468ddbdf11fedcf46ad0c4410661d5d198a3c7c28b2d81cbf8e4903edfa63de5caececda0035933d5b159e211a21317fa891cc4fdb5d7a55ca2c420759e72a39a5652212403c035ed659e636c81f1b36c4de9d53e36c9c8bea8d58a0303ec6f1525070ba9f43e000000000000000c00000000000000010000000000000084000000000000008500000000000000000000000000000001000000000000000d96ea206d6cb514d4a521403a8f703df35a343096122e178bc371f4e6f977f8b80f00000000000000010000000000000087000000000000008800000000000000000000000000000001000000000000000a3ea7ca7907af7a044eda999f335e90be84d922f9ba85be940150b43a8bb8985b0d00000000000000010000000000000085000000000000008600000000000000000000000000000001000000000000000aa2978b41a6fc1cdcecad50e11a8ced659c5488a1fd1c398236ddee36ac4fb13c0b00000000000000010000000000000074000000000000007500000000000000000000000000000001000000000000000af8a18f79bb1e69ed549129fc14446b219ff2dd9105cdcffbc259e3d877bd12501900000000000000010000000000000013010000000000003e0100000000000001000000000000002b000000000000005374726963742d5472616e73706f72742d53656375726974793a206d61782d6167653d31353736383030304c1cb0fcb15fdc9f6a3caa4e5653bc0d00717fc341bd9d28a3370751d824ad8a300000000000000001000000000000003f010000000000004001000000000000010000000000000001000000000000000a8195e9e6fb4776adad311559b8258b3e4aa71d4468e99f76d491ac6de654614c130000000000000001000000000000006c00000000000000860000000000000001000000000000001a000000000000005472616e736665722d456e636f64696e673a206368756e6b656493951defb16681d7e39b835ccf7c1ff00f55f6bab0665298cfc5d499e08aba592f0000000000000001000000000000003e010000000000003f01000000000000010000000000000001000000000000000d19eb18e0780cc4bd5f52a5462ebad35d1286aa0be47e5a018f61e25b26099b910300000000000000010000000000000000000000000000002b0000000000000000000000000000002b000000000000004745542068747470733a2f2f73776170692e6465762f6170692f70656f706c652f3120485454502f312e3191f6427b0957d686f9d019c78a4b759dc73ca58d23012ede5036f5318265afe73200000000000000010000000000000041010000000000004201000000000000010000000000000001000000000000000a185f1b2cb31096c5c0b5dee69bc955921e2af2fe004e538fbbcc8683df4986fe2a000000000000000100000000000000cd00000000000000ce00000000000000010000000000000001000000000000000a7e6e59de6e12f0d94b46c369ed893368a9d31bd835a5adbd399f2b938ec60fef2b000000000000000100000000000000f600000000000000f700000000000000010000000000000001000000000000000da561eb1758111ed2f7b7668f71d217c4c74f7d1c5801cd9bbe8e35d294eed47c0a00000000000000010000000000000073000000000000007400000000000000000000000000000001000000000000000d391781437999aab0326c51a126148d35b9269c2c284892b4b0b3d132e3009f0416000000000000000100000000000000b100000000000000cc0000000000000001000000000000001b00000000000000582d4672616d652d4f7074696f6e733a2053414d454f524947494e34bf35470e84cd2d9be6e420614d8eb6156b3cf392cf7d31168e1cecdddbd49729000000000000000100000000000000cc00000000000000cd00000000000000010000000000000001000000000000000d4f250e99698e7a42429d7697d3430ee6d97946d1ab545aa2f78dabef14a35443260000000000000001000000000000009a000000000000009b00000000000000010000000000000001000000000000000a2ad5b887a7cbdb9c0c063f37e45c373934e8dae42663414e6368da3ac2e4ab4028000000000000000100000000000000b000000000000000b100000000000000010000000000000001000000000000000a9ade1262ce9d20636872c5195612797fc63c5b128b508b731f2fa5f00c13eec8050000000000000001000000000000002c000000000000002d00000000000000000000000000000001000000000000000a2db755cd1a725ceca8f82dbaaa511f6148cb5503751ef9576e2df9f6291f1407040000000000000001000000000000002b000000000000002c00000000000000000000000000000001000000000000000d78f1fa51a78c78cedb3b417c049cf5a9cc21b8a88909af2aa3dcba6a8c8879b82d00000000000000010000000000000011010000000000001201000000000000010000000000000001000000000000000dc1be20726463291e024c5cee58f71c048b51293ed506fcd6e16cd225e3840970140000000000000001000000000000008800000000000000990000000000000001000000000000001100000000000000436f6e6e656374696f6e3a20636c6f7365c2babe38c5b1dd0f05083790b3a517b314c2856b2e514b334f7f37809c34ab4d17000000000000000100000000000000ce00000000000000f60000000000000001000000000000002800000000000000455461673a20226565333938363130343335633332386634643061346531623064326637626263221fa41cfa8a43f9c429362030ebfc485b7f22760be68a857ba363ca07f4d5c3841a00000000000000010000000000000000000000000000000f0000000000000001000000000000000f00000000000000485454502f312e3120323030204f4b5fd6cde8f919ba0651fc0fa175b1e8b0757c6059614797bee69ab28cb1306b0a1c00000000000000010000000000000010000000000000001100000000000000010000000000000001000000000000000a8d5e96737b598e8b0e066b3d41d6c75403567f8f05a1507a8aa5d57a537586d8220000000000000001000000000000006b000000000000006c00000000000000010000000000000001000000000000000ac4f99df5c308cf1ad9767055247b6bc178b58ce5083386ad7274e6ace5aacd3a2300000000000000010000000000000086000000000000008700000000000000010000000000000001000000000000000d3cb4641d2933f9188a63e9a972bfd6ed61a6072cb04b8a65bb3cef1114c07b2e39000000000000000100000000000000d303000000000000d403000000000000010000000000000001000000000000000d6809f86ac50d7ee3571e5fa6d62174fc7ea40f85b078387625c0057f01ec54d12e00000000000000010000000000000012010000000000001301000000000000010000000000000001000000000000000a824f8ac9162621f92208b25371907f6275d1b4dae50bca84deee6b79e42414b1120000000000000001000000000000004c000000000000006a0000000000000001000000000000001e00000000000000436f6e74656e742d547970653a206170706c69636174696f6e2f6a736f6e9ce594f77e4978550932f5d37bb61840ecce13cbe72423e9c223b1778ab4be5837000000000000000100000000000000d103000000000000d203000000000000010000000000000001000000000000000dc3b9d065640a3a95ff63029bc7d3f665f2bb0d0cb853aa0a3eb49f85b51968b13a000000000000000100000000000000d403000000000000d503000000000000010000000000000001000000000000000a0f807fcc122d881d8c975b095ff3ab1451c0582f44a87a8b412c68df8111d7121b0000000000000001000000000000000f000000000000001000000000000000010000000000000001000000000000000d38f49f3d34cfa61378015e4452f7cdf5daa757d5f90adfd5f90e3885c624ff7c0100000000000000010000000000000040000000000000005e0000000000000000000000000000001e00000000000000636f6e74656e742d747970653a206170706c69636174696f6e2f6a736f6eea8a9887350c64d961458aeb624f9cacbe5a3eabfc27fe586370445ad5b16a69150000000000000001000000000000009b00000000000000af0000000000000001000000000000001400000000000000566172793a204163636570742c20436f6f6b6965fc5a7ad9d7076f630ced01ffade7c60452750b0221921196c0128b9ae2c14d79080000000000000001000000000000005e000000000000005f00000000000000000000000000000001000000000000000dc223c0a0710aa6aafe2b9b63fc55c75e323c54ac09aa26d697b26e7a953d84a0000000000000000001000000000000002d000000000000003e0000000000000000000000000000001100000000000000636f6e6e656374696f6e3a20636c6f7365b1ca8bada5de8742fb4b1529cadaa44b8846172f50640b6ad8c841cf7cb71e051100000000000000010000000000000027000000000000004a0000000000000001000000000000002300000000000000446174653a204d6f6e2c2031322041756720323032342030393a30363a313420474d54d4182cd518018b0682322fb898162712db26c55b65e4a7e62e676a1cac51d0a0060000000000000001000000000000003e000000000000003f00000000000000000000000000000001000000000000000d7535c4bd59ea5028214ca997f9959c0dcef89b2c22fbad6e5c732eb4a8c201cd1000000000000000010000000000000011000000000000002500000000000000010000000000000014000000000000005365727665723a206e67696e782f312e31362e31dde166c44d35970fe58402beb14a2b8c2c76b8f9bda5acc070532f13107be1041e00000000000000010000000000000026000000000000002700000000000000010000000000000001000000000000000af89d3abf1d5865424fdb77a64717a8d91ee3cea0ee82b30599ed536ec55aa1f53100000000000000010000000000000040010000000000004101000000000000010000000000000001000000000000000d1aaafceee08d60020624b60a0d662447b5bd312d59c137b9c515e80c104fbd74200000000000000001000000000000004b000000000000004c00000000000000010000000000000001000000000000000a5840710fc7c0c7aa80c18bae02039128b0c3812783c82aa93be577915d3f70d73400000000000000010000000000000046010000000000004701000000000000010000000000000001000000000000000a12219d2d9de8ac0f025793a72012d193b9a1e88645fe728fa442ff67af3406fc0e00000000000000010000000000000086000000000000008700000000000000000000000000000001000000000000000d23ee390e5a382e87e2ff17b8bd3fba8217cc22f89258bf2803eda68d9e3ffbfb18000000000000000100000000000000f800000000000000110100000000000001000000000000001900000000000000416c6c6f773a204745542c20484541442c204f5054494f4e531c00a0bcc34c4e940f89a1b3de0a3b84434dee9e3c334ab2d88028a1e629697e3c0000000000000001000000000000007b010000000000008f010000000000000100000000000000140000000000000022686169725f636f6c6f72223a22626c6f6e64229369bfd64d9a4d0e86ccc6c7f445c02a58f73668af8849561909c2a9ac51514127000000000000000100000000000000af00000000000000b000000000000000010000000000000001000000000000000d49237ad294a8a63cae251b6d2fa87d40a07a74a9409d9e3fe36e3d709297c3f535000000000000000100000000000000ce03000000000000cf03000000000000010000000000000001000000000000000dc81c989bbafe60febde627f6a168e52fa88f7a5c92eca55c1019b6de1a5d86da36000000000000000100000000000000cf03000000000000d003000000000000010000000000000001000000000000000afd886fa5c8607003090aa3fab243fae4616c487dd15d81dd802f8a4bb67907b43300000000000000010000000000000045010000000000004601000000000000010000000000000001000000000000000d6e68c045711cf1e6af7f0f4e6d08bd228079d08848919469d3868732b6872c7e210000000000000001000000000000006a000000000000006b00000000000000010000000000000001000000000000000d7352c55880b9df7aa73a1b1d190b4a90efb11adfcb469c90ff9c8981000cb60c020000000000000001000000000000007500000000000000840000000000000000000000000000000f00000000000000686f73743a2073776170692e6465768cb75633fd51040f767560709611c315317d6413582979d4a64d5e83a51a35332400000000000000010000000000000087000000000000008800000000000000010000000000000001000000000000000a153fa7b21dfb2773ff3762b00af6821e2d0c5ec8bf533aa4f43d1f57c18b504d090000000000000001000000000000005f000000000000006000000000000000000000000000000001000000000000000aa7dfbe3c9a5bad316bb4fb9e270de851674cd014e2f599873175fcbdf58fe468070000000000000001000000000000003f000000000000004000000000000000000000000000000001000000000000000a5a6ed55ff196b48e1cfe516d9d8edd3524ec83cea8e012ad30f6cb7d9cad59a73d0000000000000001000000000000009001000000000000a3010000000000000100000000000000130000000000000022736b696e5f636f6c6f72223a226661697222ed76c9d8a2592a8558e125d57ea4ebc09c7c8c2fb295861a67ddb4feaa6362273b00000000000000010000000000000048010000000000005f0100000000000001000000000000001700000000000000226e616d65223a224c756b6520536b7977616c6b65722224d38a6c588dbd4dafd2537e4fd30524496dc27e883f5fc1bf3ee963687c52dc2500000000000000010000000000000099000000000000009a00000000000000010000000000000001000000000000000de63e2efda1904a47fbcc650d33a69a52ccc4790c0f9c4d1ce6a981bf7d599a7038000000000000000100000000000000d203000000000000d303000000000000010000000000000001000000000000000a144f0599d0f524c0c13420fcf3b0c95c4d47541cad82f7fd6fbb8b6e8bf8dc931d00000000000000010000000000000025000000000000002600000000000000010000000000000001000000000000000debc5da6e2952358474248c5271436e6cf125ff89e62de8b557936bd07d6eb2681f0000000000000001000000000000004a000000000000004b00000000000000010000000000000001000000000000000dd5a8b218331287f4c1160b03144cb80f3ab4c6dccfee0b7f4844b50c81ee86262c000000000000000100000000000000f700000000000000f800000000000000010000000000000001000000000000000afa4fc1b6cb445a8137ffb39b2c9851ab21e5ae69e344da19be301a31f77d299000000000000000003e00000000000000`;
    const proof = (await new TlsProof(proofHex)) as _TlsProof;
    const result = await proof.verify({
      typ: 'P256',
      key: `-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBv36FI4ZFszJa0DQFJ3wWCXvVLFr\ncRzMG5kaTeHGoSzDu6cFqx3uEWYpFGo6C0EOUgf+mEgbktLrXocv5yHzKg==\n-----END PUBLIC KEY-----\n`,
    });
    console.timeEnd('verify');
    console.log(result);
    assert(result.sent.includes('host: swapi.dev'));
    assert(!result.sent.includes('secret: test_secret'));
    assert(result.recv.includes('"name":"Luke Skywalker"'));
    assert(result.recv.includes('"hair_color":"blond"'));
    assert(result.recv.includes('"skin_color":"fair"'));
    // @ts-ignore
    document.getElementById('simple-verify').textContent = 'OK';
  } catch (err) {
    console.log('caught error from wasm');
    console.error(err);
    // @ts-ignore
    document.getElementById('simple-verify').textContent = err.message;
  }
})();
```
--------------------------------------------------------------------------------
/gmail_mcp/server.py:
--------------------------------------------------------------------------------
```python
# Imports
from mcp.server.fastmcp import FastMCP
from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Mount, Route
from fastapi import FastAPI
import uvicorn
import asyncio
from datetime import datetime, timedelta
import email
import imaplib
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from urllib.parse import unquote
from dataclasses import dataclass
import ssl
from loguru import logger
import sys
import uuid
from starlette.requests import Request
from weakref import WeakValueDictionary
# Setup logging
logger.remove()  # Remove default handler
logger.add(
    sys.stderr,
    format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
    level="INFO",
    colorize=True,
)
# Constants
SEARCH_TIMEOUT = 60  # seconds
MAX_EMAILS = 100
# Setup FastAPI app and MCP server
app = FastAPI()
mcp = FastMCP("Email Client")
# Global session management - using WeakValueDictionary to allow garbage collection
# and avoid memory leaks when connections close
ACTIVE_SESSIONS = WeakValueDictionary()
#################################
# DATACLASSES
#################################
@dataclass
class DateRange:
    """Date range operations for email searches"""
    start: str | None = None
    end: str | None = None
    @staticmethod
    def format_date(
        date_str: str | None, default_days_ago: int = 0
    ) -> tuple[datetime, str]:
        """Format a date string to IMAP format, with default if None"""
        if date_str:
            date_obj = datetime.strptime(date_str, "%Y-%m-%d")
        else:
            date_obj = datetime.now() - timedelta(days=default_days_ago)
        return date_obj, date_obj.strftime("%d-%b-%Y")
    def get_days(self) -> list[datetime]:
        """Get all days in the date range (inclusive)"""
        start_obj, _ = self.format_date(self.start, 7)  # Default 7 days ago
        end_obj, _ = self.format_date(self.end, 0)  # Default today
        days = []
        current = start_obj
        while current <= end_obj:
            days.append(current)
            current += timedelta(days=1)
        return days
class SearchCriteria:
    """Builder for IMAP search criteria strings"""
    @staticmethod
    def create_date_criteria(start: str | None = None, end: str | None = None) -> str:
        """Create IMAP search criteria for date range"""
        start_obj, start_fmt = DateRange.format_date(start, 7)  # Default 7 days ago
        end_obj, end_fmt = DateRange.format_date(end, 0)  # Default today
        # Build date criteria
        if start_fmt == end_fmt:
            # If searching for a single day
            return f'ON "{start_fmt}"'
        else:
            # Calculate next day for BEFORE (which is exclusive)
            next_day = (end_obj + timedelta(days=1)).strftime("%d-%b-%Y")
            return f'SINCE "{start_fmt}" BEFORE "{next_day}"'
    @staticmethod
    def create_search_criteria(
        start: str | None = None, end: str | None = None, keyword: str | None = None
    ) -> str:
        """Create complete IMAP search string for the given parameters"""
        date_criteria = SearchCriteria.create_date_criteria(start, end)
        # Add keyword if provided
        if keyword:
            keyword_criteria = f'(OR SUBJECT "{keyword}" BODY "{keyword}")'
            return f"({keyword_criteria} {date_criteria})"
        return date_criteria
@dataclass
class EmailSummary:
    """Summary information about an email"""
    id: str
    sender: str = "Unknown"
    date: str = "Unknown"
    subject: str = "No Subject"
    def __str__(self) -> str:
        """Format email summary as a table row"""
        return f"{self.id} | {self.sender} | {self.date} | {self.subject}"
    @classmethod
    def from_message_data(cls, msg_data: tuple) -> "EmailSummary":
        """Create an EmailSummary from IMAP message data."""
        email_body = email.message_from_bytes(msg_data[0][1])
        return cls(
            id=msg_data[0][0].split()[0].decode(),  # Get the email ID
            sender=email_body.get("From", "Unknown"),
            date=email_body.get("Date", "Unknown"),
            subject=email_body.get("Subject", "No Subject"),
        )
@dataclass
class EmailContent(EmailSummary):
    """Full content of an email including body"""
    to: str = "Unknown"
    content: str = ""
    def __str__(self) -> str:
        """Format email content for display"""
        return (
            f"From: {self.sender}\n"
            f"To: {self.to}\n"
            f"Date: {self.date}\n"
            f"Subject: {self.subject}\n"
            f"\nContent:\n{self.content}"
        )
    @classmethod
    def from_message_data(cls, msg_data: tuple) -> "EmailContent":
        """Create an EmailContent from IMAP message data."""
        email_body = email.message_from_bytes(msg_data[0][1])
        # Extract body content
        body = ""
        if email_body.is_multipart():
            # Handle multipart messages
            for part in email_body.walk():
                if part.get_content_type() == "text/plain":
                    body = part.get_payload(decode=True).decode()
                    break
                elif part.get_content_type() == "text/html":
                    # If no plain text found, use HTML content
                    if not body:
                        body = part.get_payload(decode=True).decode()
        else:
            # Handle non-multipart messages
            body = email_body.get_payload(decode=True).decode()
        return cls(
            id=msg_data[0][0].split()[0].decode(),
            sender=email_body.get("From", "Unknown"),
            to=email_body.get("To", "Unknown"),
            date=email_body.get("Date", "Unknown"),
            subject=email_body.get("Subject", "No Subject"),
            content=body,
        )
@dataclass
class EmailResults:
    """Container for formatted email search results"""
    emails: list[EmailSummary]
    def format_table(self) -> str:
        """Format search results as a table"""
        if not self.emails:
            return "No emails found matching the criteria."
        result = "Found emails:\n\n"
        result += "ID | From | Date | Subject\n"
        result += "-" * 80 + "\n"
        for email_ in self.emails:
            result += f"{email_}\n"
        result += "\nUse get-email-content with an email ID to view the full content of a specific email."
        return result
    @classmethod
    def format_daily_counts(cls, date_counts: list[tuple[datetime, int | str]]) -> str:
        """Format daily email counts as a table"""
        result = "Daily email counts:\n\n"
        result += "Date | Count\n"
        result += "-" * 30 + "\n"
        for date, count in date_counts:
            result += f"{date.strftime('%Y-%m-%d')} | {count}\n"
        return result
class EmailMessageFactory:
    """Utility for creating email messages"""
    @staticmethod
    def create_message(
        from_email: str, to: list[str], subject: str, content: str, cc: list[str] = None
    ) -> MIMEMultipart:
        """Create an email message"""
        msg = MIMEMultipart()
        msg["From"] = from_email
        msg["To"] = ", ".join(to)
        if cc:
            msg["Cc"] = ", ".join(cc)
        msg["Subject"] = subject
        msg.attach(MIMEText(content, "plain", "utf-8"))
        return msg
@dataclass
class EmailSession:
    """Email connection credentials and server configuration"""
    email: str
    password: str
    imap_server: str = "imap.gmail.com"
    smtp_server: str = "smtp.gmail.com"
    smtp_port: int = 587
    @classmethod
    def from_request(cls, request: Request) -> "EmailSession | None":
        """Extract email session configuration from request parameters"""
        try:
            if "ADDR" in request.query_params and "ASP" in request.query_params:
                email_addr = unquote(request.query_params["ADDR"])
                password = unquote(request.query_params["ASP"])
                return cls(email=email_addr, password=password)
            else:
                logger.error("Missing required connection parameters")
        except Exception as e:
            logger.error(f"Config extraction error: {e}")
        return None
    @classmethod
    def from_current_session(cls) -> "EmailSession | None":
        """Get the current session configuration based on the current request"""
        # First try to get from mcp._current_request
        if hasattr(mcp, "_current_request"):
            request = mcp._current_request
            return cls.from_request(request)
        # Then try server request
        elif hasattr(mcp, "_mcp_server") and hasattr(mcp._mcp_server, "_request"):
            request = mcp._mcp_server._request
            return cls.from_request(request)
        else:
            logger.error("Cannot find request object")
            return None
    async def connect_imap(self, folder: str = "inbox"):
        """Connect to IMAP server and select folder"""
        mail = imaplib.IMAP4_SSL(self.imap_server)
        mail.login(self.email, self.password)
        if folder == "sent":
            mail.select('"[Gmail]/Sent Mail"')  # For Gmail
        else:
            mail.select("inbox")
        return mail
    def close_imap(self, mail):
        """Safely close IMAP connection"""
        try:
            mail.close()
            mail.logout()
        except Exception as e:
            logger.error(f"IMAP close error: {e}")
#################################
# EMAIL ASYNC OPERATIONS
#################################
async def search_emails_async(
    mail: imaplib.IMAP4_SSL, search_criteria: str
) -> list[EmailSummary]:
    """Asynchronously search emails with timeout."""
    loop = asyncio.get_event_loop()
    try:
        _, messages = await loop.run_in_executor(
            None, lambda: mail.search(None, search_criteria)
        )
        if not messages[0]:
            return []
        email_list = []
        for num in messages[0].split()[:MAX_EMAILS]:  # Limit to MAX_EMAILS
            _, msg_data = await loop.run_in_executor(
                None, lambda: mail.fetch(num, "(RFC822)")
            )
            email_list.append(EmailSummary.from_message_data(msg_data))
        return email_list
    except Exception as e:
        raise Exception(f"Error searching emails: {str(e)}")
async def get_email_content_async(
    mail: imaplib.IMAP4_SSL, email_id: str
) -> EmailContent:
    """Asynchronously get full content of a specific email."""
    loop = asyncio.get_event_loop()
    try:
        _, msg_data = await loop.run_in_executor(
            None, lambda: mail.fetch(email_id, "(RFC822)")
        )
        return EmailContent.from_message_data(msg_data)
    except Exception as e:
        raise Exception(f"Error fetching email content: {str(e)}")
async def count_emails_async(mail: imaplib.IMAP4_SSL, search_criteria: str) -> int:
    """Asynchronously count emails matching the search criteria."""
    loop = asyncio.get_event_loop()
    try:
        _, messages = await loop.run_in_executor(
            None, lambda: mail.search(None, search_criteria)
        )
        return len(messages[0].split()) if messages[0] else 0
    except Exception as e:
        raise Exception(f"Error counting emails: {str(e)}")
async def send_email_async(
    to_addresses: list[str],
    subject: str,
    content: str,
    cc_addresses: list[str] | None = None,
    config: EmailSession = None,
) -> None:
    """Asynchronously send an email."""
    try:
        # Create message
        msg = EmailMessageFactory.create_message(
            from_email=config.email,
            to=to_addresses,
            subject=subject,
            content=content,
            cc=cc_addresses,
        )
        context = ssl.create_default_context()
        server = smtplib.SMTP(config.smtp_server, config.smtp_port)
        await asyncio.to_thread(server.starttls, context=context)
        await asyncio.to_thread(server.login, config.email, config.password)
        all_recipients = to_addresses
        if cc_addresses:
            all_recipients += cc_addresses
        await asyncio.to_thread(
            server.sendmail, config.email, all_recipients, msg.as_string()
        )
        server.quit()
    except Exception as e:
        logger.error(f"Error in send_email_async: {str(e)}")
        raise
#################################
# MCP TOOL FUNCTIONS
#################################
@mcp.tool()
async def search_emails(
    start_date: str = None,
    end_date: str = None,
    keyword: str = None,
    folder: str = "inbox",
) -> str:
    """Search emails within a date range and/or with specific keywords"""
    try:
        # Get session-specific config
        email_session = EmailSession.from_current_session()
        if not email_session:
            return "Error: No valid email configuration found for this session."
        # Connect to IMAP server using connection-specific credentials
        mail = imaplib.IMAP4_SSL(email_session.imap_server)
        mail.login(email_session.email, email_session.password)
        # Select folder
        if folder == "sent":
            mail.select('"[Gmail]/Sent Mail"')  # For Gmail
        else:
            mail.select("inbox")
        # Get search criteria
        search_criteria = SearchCriteria.create_search_criteria(
            start_date, end_date, keyword
        )
        try:
            async with asyncio.timeout(SEARCH_TIMEOUT):
                email_list = await search_emails_async(mail, search_criteria)
                # Format results using the EmailResults class
                results = EmailResults(email_list)
                return results.format_table()
        except asyncio.TimeoutError:
            return "Search operation timed out. Please try with a more specific search criteria."
        finally:
            email_session.close_imap(mail)
    except Exception as e:
        logger.error(f"Error in search_emails: {str(e)}")
        return f"An error occurred: {str(e)}"
@mcp.tool()
async def get_email_content(email_id: str) -> str:
    """Get the full content of a specific email by its ID"""
    try:
        # Get session-specific config
        email_session = EmailSession.from_current_session()
        if not email_session:
            return "Error: No valid email configuration found for this session."
        mail = await email_session.connect_imap()
        try:
            async with asyncio.timeout(SEARCH_TIMEOUT):
                email_content = await get_email_content_async(mail, email_id)
                return str(email_content)
        except asyncio.TimeoutError:
            return "Operation timed out while fetching email content."
        finally:
            email_session.close_imap(mail)
    except Exception as e:
        logger.error(f"Error in get_email_content: {str(e)}")
        return f"An error occurred: {str(e)}"
@mcp.tool()
async def count_daily_emails(start_date: str, end_date: str) -> str:
    """Count emails received for each day in a date range"""
    try:
        # Get session-specific config
        email_session = EmailSession.from_current_session()
        if not email_session:
            return "Error: No valid email configuration found for this session."
        mail = imaplib.IMAP4_SSL(email_session.imap_server)
        mail.login(email_session.email, email_session.password)
        mail.select("inbox")
        try:
            # Get all days in the range
            date_range = DateRange(start=start_date, end=end_date)
            days = date_range.get_days()
            date_counts = []
            for day in days:
                date_str = day.strftime("%d-%b-%Y")
                search_criteria = f'(ON "{date_str}")'
                try:
                    async with asyncio.timeout(SEARCH_TIMEOUT):
                        count = await count_emails_async(mail, search_criteria)
                        date_counts.append((day, count))
                except asyncio.TimeoutError:
                    date_counts.append((day, "Timeout"))
            # Format results using EmailResults class
            return EmailResults.format_daily_counts(date_counts)
        finally:
            email_session.close_imap(mail)
    except Exception as e:
        logger.error(f"Error in count_daily_emails: {str(e)}")
        return f"An error occurred: {str(e)}"
@mcp.tool()
async def send_email(
    to: list[str], subject: str, content: str, cc: list[str] = None
) -> str:
    """CONFIRMATION STEP: Actually send the email after user confirms the details."""
    if not to:
        return "At least one recipient email address is required."
    try:
        # No logging of email addresses or content
        # Get session-specific config
        email_session = EmailSession.from_current_session()
        if not email_session:
            return "Error: No valid email configuration found for this session."
        async with asyncio.timeout(SEARCH_TIMEOUT):
            await send_email_async(to, subject, content, cc, config=email_session)
            return "Email sent successfully!"
    except asyncio.TimeoutError:
        return "Operation timed out while sending email."
    except Exception as e:
        error_msg = str(e)
        return f"Failed to send email: {error_msg}\n\nPlease check:\n1. Email and password are correct\n2. SMTP settings are correct\n3. Less secure app access is enabled (for Gmail)\n4. Using App Password if 2FA is enabled"
#################################
# SERVER SETUP
#################################
def create_sse_server(mcp):
    """Create a Starlette app that handles SSE connections and message handling"""
    transport = SseServerTransport("/messages/")
    # Define handler functions
    async def handle_sse(request):
        # Generate a unique session ID for this connection
        session_id = str(uuid.uuid4())
        # No logging of connection details
        # Store request in session dictionary with unique ID
        ACTIVE_SESSIONS[session_id] = request
        async with transport.connect_sse(
            request.scope, request.receive, request._send
        ) as streams:
            try:
                # Store the request object for later access by tools
                mcp._current_request = request
                await mcp._mcp_server.run(
                    streams[0],
                    streams[1],
                    mcp._mcp_server.create_initialization_options(),
                )
            finally:
                # Clear the request reference
                if hasattr(mcp, "_current_request"):
                    delattr(mcp, "_current_request")
                # Remove from active sessions
                if session_id in ACTIVE_SESSIONS:
                    del ACTIVE_SESSIONS[session_id]
                    # No logging of session destruction
    # Create Starlette routes for SSE and message handling
    routes = [
        Route("/sse/", endpoint=handle_sse),
        Mount("/messages/", app=transport.handle_post_message),
    ]
    # Create a Starlette app
    return Starlette(routes=routes)
@app.get("/")
def read_root():
    """Health check endpoint"""
    return {"status": "Email Client Server is running"}
# Mount the Starlette SSE server onto the FastAPI app
app.mount("/", create_sse_server(mcp))
# Start the server
if __name__ == "__main__":
    # Minimal logging that doesn't reveal user information
    uvicorn.run(app, host="0.0.0.0", port=7047)
```