---
sidebar_position: 2
sidebar_label: Verifier Server
---
# Verifier Server

The TLSNotary Verifier Server is a Rust-based HTTP/WebSocket server that acts as the **verifier party** in Multi-Party Computation TLS (MPC-TLS). It validates cryptographic proofs generated by the browser extension without ever seeing the user's private data.

## Why a Verifier Server?

TLSNotary uses **Multi-Party Computation (MPC)** to generate cryptographic proofs of TLS data. This requires two parties:

1. **Prover** (Browser Extension) — the user generating the proof
2. **Verifier** (This Server) — an independent party that validates the proof

### What the Verifier Does

- **Participates in MPC-TLS handshake** — co-signs the TLS handshake without seeing plaintext data
- **Validates transcript integrity** — ensures the HTTP request/response hasn't been tampered with
- **Generates proof** — creates cryptographic proof that data came from a genuine TLS connection
- **Forwards requests** — acts as a WebSocket proxy to route TLS traffic to target servers

### What the Verifier Does NOT See

- **User credentials** — authentication tokens, cookies, and API keys remain encrypted
- **Hidden data** — only data marked with `action: 'REVEAL'` in [handlers](./plugins.md#handler-structure) is visible
- **Committed data** — data marked with `action: 'PEDERSEN'` is sent as a hash commitment, not plaintext

---

## Architecture

```mermaid
sequenceDiagram
    participant B as Browser Extension<br/>(Prover)
    participant V as Verifier Server<br/>(packages/verifier)
    participant P as WebSocket Proxy<br/>(optional, any proxy)
    participant T as Target Server<br/>(api.x.com, etc.)
    participant BE as Backend

    Note over P: Proxy can run on verifier,<br/>user's device, or separately

    B->>V: 1. /session<br/>(create session)
    V-->>B: sessionId
    B->>V: 2. /verifier?sessionId=...<br/>(MPC-TLS verification)
    B->>P: 3. /proxy?token=api.x.com<br/>(via WebSocket)
    P->>T: Forward request<br/>(TCP/TLS)
    T-->>P: Response
    P-->>B: Response
    V-->>B: Proof Result
    V->>BE: 4. Webhook (optional)
```

**Key components:**

1. **Session Management** — Thread-safe HashMap stores session configuration
2. **WebSocket Endpoints** — `/session`, `/verifier`, and `/proxy` handle different protocol stages
3. **MPC-TLS Verification** — Uses `tlsn` crate for cryptographic verification
4. **Built-in Proxy** — WebSocket-to-TCP bridge forwards requests to target servers
5. **Webhook System** — Fire-and-forget POST notifications with redacted transcripts

---

## Built-in WebSocket Proxy

:::note
The recommended approach is to run a WebSocket-to-TCP proxy on the prover's device for maximum control and privacy. However, this requires setup effort from the user. For convenience, the verifier includes a built-in proxy that works out-of-the-box. Any WebSocket-to-TCP proxy can be used — you can run your own, use a third-party service, or use the verifier's built-in proxy.
:::

The verifier includes a built-in proxy that eliminates the need for external proxy services.

```mermaid
graph LR
    A[Browser Extension] -->|WebSocket| B[Verifier Proxy]
    B -->|TCP/TLS| C[api.x.com]
```

**Endpoint:** `ws[s]://<host>/proxy?token=<target-hostname>`

| Environment | URL                                              |
| ----------- | ------------------------------------------------ |
| Local       | `ws://localhost:7047/proxy?token=api.x.com`      |
| Production  | `wss://demo.tlsnotary.org/proxy?token=api.x.com` |

The `token` parameter specifies the target server hostname. The proxy:

- Parses hostname and port from the `token` parameter (defaults to port 443)
- Establishes a TCP connection to the target server
- Bridges bidirectionally: WebSocket messages ↔ TCP stream
- Cleans up automatically when either side disconnects
- Logs total bytes forwarded for debugging

---

## API Endpoints

### Health Check

**GET** `/health`

```bash
curl http://localhost:7047/health
# ok
```

### Create Session

**WebSocket** `/session`

Creates a new MPC-TLS verification session.

**Message (JSON):**
```json
{
  "maxRecvData": 16384,
  "maxSentData": 4096,
  "sessionData": {
    "userId": "user_123",
    "purpose": "twitter_verification"
  }
}
```

| Field         | Type   | Default | Description                                        |
| ------------- | ------ | ------- | -------------------------------------------------- |
| `maxRecvData` | number | 16384   | Maximum bytes the prover can receive               |
| `maxSentData` | number | 4096    | Maximum bytes the prover can send                  |
| `sessionData` | object | —       | Custom metadata, included in webhook notifications |

**Response:**
```json
{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000"
}
```

**Session lifecycle:**
1. Extension opens WebSocket to `/session` and sends configuration
2. Server generates UUID, stores config, returns `sessionId`
3. Extension opens second WebSocket to `/verifier?sessionId=<id>`
4. After verification completes, session is cleaned up

### Verifier Connection

**WebSocket** `/verifier?sessionId=<session-id>`

Connects to an existing session as the verifier party. Validates the `sessionId`, retrieves session config, spawns the MPC-TLS verification task, and returns redacted transcripts.

Sessions are single-use and automatically removed after the WebSocket closes.

### WebSocket Proxy

**WebSocket** `/proxy?token=<host>`

See [Built-in WebSocket Proxy](#built-in-websocket-proxy) above.

---

## Session Flow

Complete flow for generating a TLS proof:

```mermaid
sequenceDiagram
    participant E as Extension
    participant V as Verifier Server
    participant P as WebSocket Proxy
    participant T as Target Server
    participant BE as Backend

    Note over E,V: Session Setup
    E->>V: 1. WS /session<br/>(maxRecvData, maxSentData, sessionData)
    V-->>E: 2. { sessionId: "..." }

    Note over E,V: MPC-TLS Verification Connection
    E->>V: 3. WS /verifier?sessionId=...
    activate V

    Note over E,V: Cryptographic Protocol
    E->>V: 4. MPC-TLS handshake<br/>(co-sign TLS without seeing plaintext)

    Note over E,T: HTTP Request/Response (via Proxy)
    E->>P: 5. WS /proxy?token=api.x.com
    activate P
    P->>T: 6. TCP: HTTP request<br/>(headers, body)
    activate T
    T-->>P: 7. TCP: HTTP response<br/>(headers, body, status)
    deactivate T
    P-->>E: Forward response
    deactivate P

    Note over V,E: Proof Generation
    V-->>E: 8. Transcript validation<br/>(verify request/response integrity)
    V-->>E: 9. Proof result<br/>(redacted transcript per handlers)
    deactivate V

    opt Webhook Configured
        Note over V,BE: Backend Integration
        V->>BE: 10. Webhook POST<br/>(sessionId, redacted transcript, revealConfig)
    end

    Note over V,E: Cleanup
    V-->>E: 11. WS close, session cleanup
```

---

## Webhook System

The verifier can send proof data to external services via HTTP POST. This enables backend integration for proof verification, storage, or business logic.

### Configuration

Webhooks are configured in `config.yaml`:

```yaml
webhooks:
  # Per-server webhooks (matched by target hostname)
  "api.x.com":
    url: "https://your-backend.example.com/webhook/twitter"
    headers:
      Authorization: "Bearer your-secret-token"
      X-Source: "tlsn-verifier"
      Content-Type: "application/json"

  "api.github.com":
    url: "https://your-backend.example.com/webhook/github"
    headers:
      Authorization: "Bearer another-token"

  # Wildcard: catch-all for any unmatched server
  "*":
    url: "https://your-backend.example.com/webhook/default"
    headers:
      X-Source: "tlsn-verifier"
```

**Matching logic:** exact hostname match first, then wildcard `"*"` fallback, then skip (no error).

### Payload

```json
{
  "sessionId": "550e8400-e29b-41d4-a716-446655440000",
  "sessionData": {
    "userId": "user_123",
    "requestId": "req_abc",
    "purpose": "account_verification"
  },
  "server_name": "api.x.com",
  "redactedTranscript": {
    "sent": "<base64-encoded request with redactions>",
    "recv": "<base64-encoded response with redactions>"
  },
  "revealConfig": [
    { "type": "SENT", "part": "START_LINE", "action": "REVEAL" },
    { "type": "RECV", "part": "BODY", "action": "REVEAL", "params": { "type": "json", "path": "screen_name" } }
  ]
}
```

The `redactedTranscript` contains **only data marked as revealed** in the handlers. Data with `action: 'PEDERSEN'` or unmarked data is not included.

### Example Backend Handler

```javascript
// Express.js webhook endpoint
app.post('/webhook/twitter', async (req, res) => {
  const { sessionId, sessionData, server_name, redactedTranscript, revealConfig } = req.body;

  // Decode base64 transcripts
  const sentData = Buffer.from(redactedTranscript.sent, 'base64').toString('utf-8');
  const recvData = Buffer.from(redactedTranscript.recv, 'base64').toString('utf-8');

  console.log('Session:', sessionId);
  console.log('User ID:', sessionData.userId);
  console.log('Revealed request:', sentData);
  console.log('Revealed response:', recvData);

  await db.proofs.insert({
    sessionId,
    userId: sessionData.userId,
    server: server_name,
    sentData,
    recvData,
    timestamp: new Date(),
  });

  res.status(200).json({ received: true });
});
```

### Behavior

Webhooks are **fire-and-forget**:

- Sent asynchronously in a separate `tokio::spawn` task
- Errors are logged but do not affect the proof result
- No retry logic (single attempt)
- Default HTTP client timeout (~30 seconds)

If the webhook endpoint is down, the proof still succeeds. Check verifier logs for webhook errors.

---

## Deployment

### Local Development

```bash
cd packages/verifier
cargo run
# Server starts on http://0.0.0.0:7047
```

### Production Build

```bash
cargo build --release
./target/release/tlsn-verifier-server
```

### Docker

For a complete Docker deployment example with docker-compose, see the [official docker-compose.yml](https://github.com/tlsnotary/tlsn-extension/blob/main/packages/demo/docker-compose.yml) in the repository.

Quick start with docker-compose:

```bash
git clone https://github.com/tlsnotary/tlsn-extension.git
cd tlsn-extension/packages/demo
docker-compose up verifier
# Server starts on http://0.0.0.0:7047
```

---

## Configuration

### Server Settings

- **Host:** `0.0.0.0` (all interfaces)
- **Port:** `7047`
- **Source:** `packages/verifier/src/main.rs`

### Webhook Configuration

**Location:** `packages/verifier/config.yaml`

- Server loads `config.yaml` from the current working directory
- If not found, webhooks are disabled (no error)
- Invalid YAML causes startup failure

### CORS

Default: permissive (allows all origins). For production, restrict to specific origins:

```rust
let cors = CorsLayer::new()
    .allow_origin("https://demo.tlsnotary.org".parse::<HeaderValue>().unwrap())
    .allow_methods([Method::GET, Method::POST])
    .allow_headers(Any);
```

### Logging

Set via `RUST_LOG` environment variable:

```bash
RUST_LOG=info cargo run     # Recommended for production
RUST_LOG=debug cargo run    # Detailed logs
RUST_LOG=trace cargo run    # Very verbose
```

---

## Development

### Project Structure

```
packages/verifier/
├── src/
│   ├── main.rs              # HTTP server, routing, WebSocket handlers
│   ├── verifier.rs          # MPC-TLS verification logic
│   ├── axum_websocket.rs    # WebSocket-to-AsyncRead/Write bridge
│   └── tests/               # Integration tests
├── Cargo.toml
├── Cargo.lock
├── config.yaml              # Webhook configuration
└── Dockerfile
```

### Testing

```bash
cargo test                      # Run all tests
cargo test -- --nocapture       # With output
cargo test test_name            # Specific test
```

### Testing with the Extension

1. Start the verifier:
   ```bash
   cd packages/verifier
   RUST_LOG=debug cargo run
   ```

2. Build and load the extension:
   ```bash
   cd packages/extension
   npm run dev
   # Load from packages/extension/build/ in chrome://extensions/
   ```

3. Open the Developer Console (right-click extension icon), paste plugin code, and click Execute.

4. Check verifier logs:
   ```
   [Session] New session created: 550e8400-...
   [Proxy] New proxy request for host: api.x.com
   [550e8400] Successfully proxied 1234 bytes
   Verification complete
   ```

### Common Issues

| Issue                                  | Solution                                                                   |
| -------------------------------------- | -------------------------------------------------------------------------- |
| `Address already in use (os error 48)` | Kill existing process: `lsof -ti:7047 \| xargs kill -9`                    |
| `failed to load config.yaml`           | Create `config.yaml` or run from the directory containing it               |
| WebSocket upgrade fails                | Ensure client sends `Upgrade: websocket` and `Connection: Upgrade` headers |
| Proxy connection timeout               | Check target server is reachable: `nc -zv api.x.com 443`                   |
