# Overview

ZDrive is a private AI inference platform where your data never leaves your device. You encrypt locally, send only ciphertext to the network, and rely on hardware-enforced isolation — Intel TDX Trusted Execution Environments — to ensure even the operator cannot read your inputs or outputs.

The platform combines three core guarantees:

* **Client-side encryption** — your encryption key is derived from your wallet signature and never leaves your browser
* **On-chain attestation** — you can cryptographically verify the TEE and model running your inference
* **Permanent decentralized storage** — encrypted results persist on Arweave forever, owned by you alone

## Who uses ZDrive?

Developers building agents that handle sensitive data, researchers working with private datasets, enterprises shipping LLM-powered features without surrendering data governance, and anyone running inference at scale without trusting a third party with raw inputs.

The business model is transparent: pay-as-you-go credits for inference, optional vault uploads to Arweave. Anonymous users get a limited free tier. Connected wallets unlock a modest free quota. Paid tiers access the full model stack and bypass datacenter restrictions.

## Core stack

| Layer          | Technology                                |
| -------------- | ----------------------------------------- |
| AI Inference   | Chutes.ai (Intel TDX TEE, Bittensor SN64) |
| Storage        | Arweave via Irys node2                    |
| Encryption     | AES-256-GCM (client-side, browser)        |
| Identity       | Base Wallet (ERC-1271)                    |
| Credits        | ZDriveXCreditsV2 on Base (UUPS proxy)     |
| Infrastructure | Cloudflare Workers + KV                   |


# How It Works

Start by connecting your wallet — no signup, no email, just sign a message to prove ownership. ZDrive derives a unique encryption key from your wallet signature; this key never leaves your browser and is never stored anywhere, even by ZDrive. You're now authenticated for the session.

Type a prompt or upload a file. Your browser immediately encrypts everything with AES-256-GCM using your derived key. The encrypted blob and metadata (which model, which tier) go to the ZDrive worker. The worker never sees plaintext — only ciphertext plus metadata. It verifies your wallet signature, checks your credit balance, routes your request to an available inference provider, and streams back the encrypted response. Your browser decrypts the response locally. You see the result; the network saw only encrypted noise.

If you want to store your encrypted data permanently, the worker signs the ciphertext as an ANS-104 DataItem and uploads it to Arweave via Irys node2. The network confirms the upload and returns an Arweave transaction ID. That ciphertext is now immutable and globally retrievable — but only you can decrypt it because only you have the encryption key.

For inference, ZDrive routes your request through a fallback chain of TEE-backed providers. If the primary model is overloaded or unavailable, the worker silently retries the next option in the chain. You get a response header (`X-Zdrive-Model-Used`) telling you which model processed your query. Credits are consumed after successful inference, not before.

## End-to-end flow

```mermaid
sequenceDiagram
    participant B as Browser
    participant W as CF Worker
    participant KV as KV Store
    participant BC as Base Chain
    participant T as Chutes TEE
    participant I as Irys / Arweave

    B->>B: Derive AES key from wallet signature
    B->>W: POST /v1/chat/completions {wallet, sig, session, messages}
    W->>W: Verify wallet signature (ERC-1271)
    W->>BC: Read credit balance
    W->>KV: Check rate limit
    alt Paid tier (credits > 0)
        W->>W: Use selected model
    else Free tier
        W->>W: Override to Qwen3-32B
    end
    W->>T: Forward inference (SSE stream)
    T-->>W: Stream response chunks
    W-->>B: Stream response (X-Zdrive-Model-Used header)
    B->>B: Decrypt response locally
    W->>BC: consumeCredit() async
    opt Vault upload
        B->>B: AES-256-GCM encrypt file
        B->>W: POST /vault/upload {ciphertext, x-vault-tags}
        W->>I: Signed ANS-104 DataItem
        I-->>W: {id: arweaveTxId}
        W-->>B: {id: arweaveTxId}
    end
```


# System Overview

ZDrive is built on a Cloudflare Worker that acts as the routing and trust boundary layer between the browser client and all backend services. It handles request routing, wallet identity verification, rate limiting, credit checks, and proxying to TEE inference and Arweave storage.

## Component diagram

```mermaid
graph TB
    subgraph Client["Browser"]
        UI[React SPA]
        AES["AES-256-GCM\nEncryption"]
        ONNX["ONNX Embeddings\n(all-MiniLM-L6-v2)"]
        WM["Base Wallet\n(ERC-1271)"]
    end

    subgraph CF["Cloudflare Edge"]
        W["CF Worker\nzdrive.io"]
        KV["KV Store\n(rate limits)"]
        ASSETS["CF Assets\n(app.zdrive.io SPA)"]
    end

    subgraph Inference["TEE Inference"]
        CHUTES["Chutes.ai\nIntel TDX TEE"]
    end

    subgraph Storage["Decentralized Storage"]
        IRYS["Irys node2"]
        ARWEAVE["Arweave\n(permanent)"]
    end

    subgraph Chain["Base Network"]
        CONTRACT["ZDriveXCreditsV2\n(UUPS Proxy)"]
    end

    UI -->|"Encrypted requests"| W
    WM -->|"ERC-1271 signature"| W
    W -->|"Rate check"| KV
    W -->|"Credit check / consume"| CONTRACT
    W -->|"Proxy inference (SSE)"| CHUTES
    W -->|"Signed DataItem"| IRYS
    IRYS --> ARWEAVE
    ASSETS -->|"SPA bundle"| UI
    AES -.->|"Ciphertext only"| W
    ONNX -.->|"Browser-side RAG"| UI
```

## Request routing

The CF Worker inspects the hostname and path to route every request:

```mermaid
flowchart TD
    REQ[Incoming Request] --> HOST{Hostname?}
    HOST -->|"app.* or stg-app.*"| SPA[Serve SPA from CF Assets]
    HOST -->|"zdrive.io / stg.zdrive.io"| PATH{Path?}
    PATH -->|"/v1/* or /vault/*"| API[API Handler]
    PATH -->|"/robots.txt"| ROBOTS[robots.txt]
    PATH -->|"/sitemap.xml"| SITEMAP[sitemap.xml]
    PATH -->|"/llms.txt"| LLMS[llms.txt]
    PATH -->|"*"| LANDING[Landing Page HTML]
    API --> AUTH{Auth valid?}
    AUTH -->|No| 401
    AUTH -->|Yes| TIER{Tier?}
    TIER -->|"Paid"| INFERENCE[Full model inference]
    TIER -->|"Free / Connected"| FREE[Qwen3-32B inference]
```

## Environments

| Environment | Domain                                | Notes                      |
| ----------- | ------------------------------------- | -------------------------- |
| Production  | `zdrive.io` / `app.zdrive.io`         | Base mainnet, live credits |
| Staging     | `stg.zdrive.io` / `stg-app.zdrive.io` | Base Sepolia testnet       |


# AI Inference & TEE

Inference runs inside Intel TDX (Trusted Domain Extensions) hardware, provided by Chutes.ai. A TEE is a physical CPU region isolated from the operating system, hypervisor, and platform operator. Code and data inside a TEE are encrypted at the silicon level — the operator cannot read memory, inspect variables, or intercept outputs. The hardware enforces this cryptographically.

ZDrive uses a tiered fallback chain to maximize availability. If a model is overloaded or returns a 429/503, the worker automatically retries the next model in the chain without any client-side intervention.

## Model availability

| Tier     | Primary       | Fallback chain                                                              |
| -------- | ------------- | --------------------------------------------------------------------------- |
| **Paid** | User-selected | DeepSeek-V3.1-TEE → DeepSeek-R1-0528-TEE → MiniMax-M2.5-TEE → Qwen3-32B-TEE |
| **Free** | Qwen3-32B-TEE | MiniMax-M2.5-TEE                                                            |

The active model is returned in the `X-Zdrive-Model-Used` response header on every inference call.

## Inference flow

```mermaid
sequenceDiagram
    participant B as Browser
    participant W as CF Worker
    participant KV as KV Store
    participant BC as Base Chain
    participant T as Chutes TEE

    B->>W: POST /v1/chat/completions
    Note over B,W: {x_wallet, x_wallet_sig, x_session, messages, model}
    W->>W: verifyWalletIdentity() via ERC-1271
    W->>BC: credits(wallet) → balance
    W->>KV: Check rate limit key
    alt balance > 0 (Paid)
        W->>W: Use requested model
        W->>KV: Increment hourly burst counter
    else balance == 0 (Free / Connected)
        W->>W: Override model → Qwen3-32B-TEE
        W->>W: Check datacenter ASN block
        W->>KV: Increment daily counter
    end
    loop Fallback chain
        W->>T: POST /chat/completions {model, messages, stream: true}
        alt 200 OK
            T-->>W: SSE stream
            W-->>B: SSE stream (X-Zdrive-Model-Used header)
            W->>BC: consumeCredit(wallet) async
        else 429 / 503
            W->>W: Try next model in chain
        else Other error
            W-->>B: Error response (no retry)
        end
    end
```

## Attestation

Every TEE model has a `chute_id` that maps to a running enclave on Chutes.ai infrastructure. The attestation endpoint verifies:

1. **TDX quote** — cryptographic proof the enclave is genuine Intel TDX hardware
2. **GPU evidence** — proof inference ran on a confidential GPU (NVIDIA Blackwell/Hopper)
3. **Model hash** — SHA-256 of the TDX quote, serving as a fingerprint of the running binary

```mermaid
sequenceDiagram
    participant B as Browser
    participant W as CF Worker
    participant C as Chutes API

    B->>W: GET /v1/attestation/report?model=deepseek-ai/DeepSeek-V3.1-TEE
    W->>W: Look up chute_id for model
    W->>C: GET /chutes/{chuteId}/evidence?nonce={32-byte random hex}
    C-->>W: {evidence: [{quote, gpu_evidence, instance_id}]}
    W->>W: Check quote present → tdx_verified = true
    W->>W: Check gpu_evidence present → gpu_verified = true
    W->>W: Extract GPU arch (e.g. BLACKWELL)
    W->>W: SHA-256(TDX quote) → model_hash
    W-->>B: {gpu_tee_verified, fpif_verified, model_hash, gpu_arch, instance_count}
```

> **Note:** Attestation proves the TEE ran the claimed code in isolated hardware. It does not prove the model's output is correct or unbiased — only that the execution environment was tamper-resistant.

## Input limits

| Tier | Max input chars | Equivalent tokens (approx) |
| ---- | --------------- | -------------------------- |
| Free | 16,000          | \~4,000                    |
| Paid | 64,000          | \~16,000                   |


# Encryption & Storage

Your browser generates an encryption key derived from your wallet signature using standard key derivation. This key is ephemeral — it lives only in the browser's JavaScript context, is never serialized, never persisted, and never sent to any server. If you reload the page, the browser re-derives the same key deterministically from a fresh signature.

Before any data leaves your device, your browser encrypts it with AES-256-GCM. The ciphertext is the only thing that reaches the ZDrive worker. Even if the worker's infrastructure were fully compromised, attackers would get encrypted blobs with no key material.

For vault storage, encrypted ciphertext is bundled into an ANS-104 DataItem, signed by the operator's key, and uploaded to Arweave via Irys node2. Once mined, the data is replicated across thousands of Arweave nodes globally and cannot be deleted, modified, or taken offline by any provider. The Arweave transaction ID is returned to your browser as proof of upload.

Permanence is the key property. Unlike cloud storage, Arweave data outlasts the provider. You own the transaction ID; you can retrieve your encrypted vault from any Arweave gateway. Since only your browser holds the decryption key, the data is cryptographically useless to everyone else.

## Vault upload flow

```mermaid
sequenceDiagram
    participant B as Browser
    participant W as CF Worker
    participant BC as Base Chain
    participant I as Irys node2
    participant A as Arweave

    B->>B: Derive AES key from wallet signature
    B->>B: AES-256-GCM encrypt(plaintext) → ciphertext
    B->>W: POST /vault/upload
    Note over B,W: Headers: x-wallet-address, x-wallet-sig, x-wallet-session, x-vault-tags
    Note over B,W: Body: ciphertext (binary)
    W->>W: verifyWalletIdentity()
    W->>BC: credits(wallet) → must be > 0
    alt Credits == 0
        W-->>B: 402 — purchase credits to unlock vault
    end
    W->>W: Parse x-vault-tags (Space token ID, etc.)
    W->>W: createSignedDataItem(ciphertext, operatorKey, tags)
    W->>I: POST /tx/ethereum (ANS-104 DataItem binary)
    I->>A: Bundle + settle fees → permanent storage
    I-->>W: {id: arweaveTxId}
    W-->>B: {id: arweaveTxId}
    Note over B: Only ciphertext ever left the browser
```

## Key derivation model

```mermaid
flowchart LR
    A["Wallet\n(private key)"] -->|"Sign auth message"| B["Signature bytes"]
    B -->|"Key derivation function"| C["AES-256-GCM key\n(ephemeral, browser-only)"]
    C -->|"encrypt(plaintext)"| D["Ciphertext"]
    D -->|"Sent to worker"| E["Irys / Arweave"]
    C -.->|"Never leaves browser"| VOID["✗ Server\n✗ Worker\n✗ Database"]
```

## Storage properties

| Property             | Value                                          |
| -------------------- | ---------------------------------------------- |
| Encryption algorithm | AES-256-GCM                                    |
| Key storage          | None — derived on demand from wallet signature |
| Upload target        | Arweave via Irys node2                         |
| DataItem format      | ANS-104 (Arweave standard)                     |
| Signer               | Operator ETH key (Irys node2 balance)          |
| Retrieval            | Any Arweave gateway using TX ID                |
| Deletability         | None — Arweave is permanent                    |
| Who can decrypt      | Only the wallet that encrypted                 |


# Identity & Authentication

ZDrive does not use accounts, passwords, or usernames. Your wallet is your identity. To start a session, you sign a message containing your address and a session UUID. The worker verifies the signature and grants access scoped to that session.

## Auth message format

```
ZDriveX Auth
Address: 0x{your_address_lowercase}
Session: {uuid_v4}
This signature verifies wallet ownership and does not authorize any transactions.
```

The session UUID binds the signature to the current browser session only. If an attacker intercepts your signature, they can only replay it within that specific session — not across sessions. The UUID is discarded when you close the app.

## Verification flow

```mermaid
sequenceDiagram
    participant B as Browser
    participant W as CF Worker
    participant BC as Base Chain

    B->>B: Generate session UUID
    B->>B: Build auth message (address + UUID)
    B->>B: wallet.signMessage(authMessage)
    B->>W: Request with {x_wallet, x_wallet_sig, x_session}
    W->>W: Rebuild expected message
    alt EOA wallet
        W->>W: ECDSA recover → compare address
    else Smart contract wallet (ERC-1271)
        W->>BC: isValidSignature(hash, sig)
        BC-->>W: 0x1626ba7e (valid) or revert
    end
    alt Signature valid
        W->>W: verifiedWallet = address
        W->>W: Proceed with tier check
    else Invalid
        W->>W: Treat as anonymous (session-based)
    end
```

## ERC-1271 support

ZDrive uses viem's `verifyMessage()` which handles both standard EOA wallets (ECDSA signature recovery) and smart contract wallets (ERC-1271 `isValidSignature` on-chain call). This means Coinbase Smart Wallet, Safe, and other account abstraction wallets work out of the box — no separate code path needed.

> **Why not just ECDSA recovery?** Smart contract wallets like Coinbase Smart Wallet do not have a private key that produces a recoverable ECDSA signature. They use ERC-1271 instead. Calling `recoverMessageAddress` on a smart wallet signature silently returns a wrong address, which would block all smart wallet users without ever surfacing an error.

## Session lifecycle

```mermaid
stateDiagram-v2
    [*] --> Anonymous: No wallet connected
    Anonymous --> Connected: Connect wallet + sign auth message
    Connected --> Paid: Purchase credits (on-chain)
    Paid --> Connected: Credits depleted
    Connected --> Anonymous: Disconnect wallet
    Anonymous --> [*]: Session expires (1h / 10 queries)
```


# Tier System

ZDrive has three access tiers based on wallet connection and credit balance.

## Tier comparison

|                      | Anonymous         | Connected                                  | Paid                  |
| -------------------- | ----------------- | ------------------------------------------ | --------------------- |
| **Requirement**      | None              | Wallet + minimum on-chain activity on Base | Wallet + credits > 0  |
| **Query limit**      | 10 / session (1h) | 25 / day                                   | 100 / hour (burst)    |
| **Models**           | Qwen3-32B only    | Qwen3-32B, MiniMax-M2.5                    | All TEE models        |
| **Vault upload**     | No                | No                                         | Yes                   |
| **Datacenter block** | Yes               | Yes                                        | No (agent use)        |
| **Rate key**         | Session UUID      | Wallet address + date                      | Wallet address + hour |

## Tier resolution flow

```mermaid
flowchart TD
    REQ[Request] --> WALLET{Wallet signature\npresent?}
    WALLET -->|No| SESSION{Session token\npresent?}
    SESSION -->|No| DENY401[401 No identity]
    SESSION -->|Yes| ASNCHECK1{Datacenter\nASN?}
    ASNCHECK1 -->|Yes| DENY403[403 Datacenter blocked]
    ASNCHECK1 -->|No| IPLIMIT{IP daily session\nlimit reached?}
    IPLIMIT -->|Yes| DENY429[429 Connect wallet\nto continue]
    IPLIMIT -->|No| SESSLIMIT{Session query\ncount ≥ 10?}
    SESSLIMIT -->|Yes| DENY429b[429 Free limit reached]
    SESSLIMIT -->|No| FREE[Free tier — Qwen3-32B]

    WALLET -->|Yes| BALANCE{credits > 0?}
    BALANCE -->|Yes| HOURLY{Hourly burst\n≥ 100?}
    HOURLY -->|Yes| DENY429c[429 Hourly limit]
    HOURLY -->|No| PAID[Paid tier — full models]
    BALANCE -->|No| ASNCHECK2{Datacenter\nASN?}
    ASNCHECK2 -->|Yes| DENY403b[403 Datacenter blocked]
    ASNCHECK2 -->|No| TXNCHECK{On-chain activity\nthreshold met?}
    TXNCHECK -->|Yes| DAILY25{Daily count\n≥ 25?}
    TXNCHECK -->|No| DAILY10{Daily count\n≥ 10?}
    DAILY25 -->|Yes| DENY429d[429 Daily limit]
    DAILY25 -->|No| CONNECTED[Connected tier]
    DAILY10 -->|Yes| DENY429e[429 Daily limit]
    DAILY10 -->|No| CONNECTED
```

## Notes

**Wallet age gate:** Connected wallets with insufficient on-chain activity on Base receive the same query limit as anonymous users. This mitigates automated abuse via freshly created wallets.

**Datacenter block:** Requests from known datacenter ASNs are blocked on free and connected tiers. Paid users bypass this — agent and automated workloads are a paid use case.

**Session expiry:** Anonymous sessions expire after 1 hour regardless of query count (KV TTL = 3600s). Connected and paid tiers use daily/hourly keys that reset at UTC midnight/hour.


# Credit System

Credits are managed by the `ZDriveXCreditsV2` smart contract on Base, deployed as a UUPS upgradeable proxy. You purchase credits by sending USDC to the contract. Each successful inference call consumes one credit, deducted asynchronously after the response is streamed.

Credits do not expire. They are stored on-chain and readable by anyone via the contract's public `credits(address)` function.

## Credit flow

```mermaid
flowchart LR
    subgraph Purchase
        U[User] -->|"USDC on Base"| C["ZDriveXCreditsV2\nSmart Contract"]
        C -->|"credits[wallet]++"| BAL[On-chain balance]
    end

    subgraph Inference
        BAL -->|"Worker reads balance\n(viem + RPC fallback)"| W[CF Worker]
        W -->|"credits > 0 → Paid tier"| INF[TEE Inference]
        INF -->|"Success"| CONSUME["consumeCredit(wallet)\nasync"]
        CONSUME -->|"credits[wallet]--"| BAL
    end
```

## RPC resilience

The worker reads balances via a viem fallback transport across multiple Base RPC providers to avoid single-provider outages. If all providers fail during a credit check, the request returns 503 — the worker fails closed rather than serving inference to a user whose balance cannot be verified.

## consumeCredit behaviour

Credit deduction happens **after** a successful inference response, not before. This means:

* Failed or errored inference calls do not cost credits
* If `consumeCredit()` fails transiently, the worker retries automatically before giving up

## Contract interface

```solidity
function credits(address user) external view returns (uint256);
function consumeCredit(address user) external;
```

Only the operator address (the CF Worker's signing key) can call `consumeCredit`. The `credits` view function is public and can be queried by anyone to verify a user's balance.


# Security Model

## What ZDrive cannot see

| Data                | Reason                                                               |
| ------------------- | -------------------------------------------------------------------- |
| Plaintext inputs    | AES-256-GCM encrypted in browser before transmission                 |
| Plaintext outputs   | TEE-enforced hardware isolation; operator cannot read enclave memory |
| Vault contents      | Encrypted client-side; worker only receives ciphertext               |
| Your encryption key | Derived from wallet signature; never leaves browser context          |
| Inference semantics | TEE operator cannot inspect model inputs/outputs at hardware level   |

## What ZDrive can see

| Data                             | Notes                                            |
| -------------------------------- | ------------------------------------------------ |
| Wallet address                   | Public by design                                 |
| Query frequency and model choice | Request metadata for billing and abuse detection |
| Credit balance                   | On-chain, visible to anyone                      |
| Arweave TX IDs                   | Immutable upload receipts                        |
| Encrypted ciphertext             | Useless without the decryption key               |
| IP address / ASN                 | Used for datacenter blocking; not persisted      |

> If you use the same wallet across sessions, your query pattern is linkable on-chain. For correlation resistance, use a dedicated wallet or Tor.

## Trust boundaries

```mermaid
graph TD
    subgraph "You control"
        KEY["Encryption key\n(wallet-derived)"]
        WALLET["Wallet private key"]
        ARWEAVETX["Arweave TX ID\n(retrieve from any gateway)"]
    end

    subgraph "Verified on-chain"
        CREDITS["Credit balance\n(Base contract)"]
        ATT["TEE attestation\n(TDX quote + model hash)"]
    end

    subgraph "Trust: Chutes.ai hardware"
        TEE["Intel TDX isolation\n(hardware-enforced)"]
    end

    subgraph "Trust: Arweave network"
        PERM["Data permanence\n(consensus-backed)"]
    end

    subgraph "Trust: ZDrive operator"
        ROUTING["Request routing\ncorrectness"]
        IRYSSIGN["Irys DataItem signing\n(operator key)"]
    end

    KEY --> ATT
    WALLET --> KEY
    CREDITS --> TEE
    TEE --> PERM
```

## Threat model

**Compromised CF Worker** An attacker with worker access can see request metadata (wallet address, timestamps, model choice) and perform denial-of-service. They cannot decrypt vault contents (no key), cannot read inference inputs/outputs (TEE isolation), and cannot forge credits (on-chain). Impact: metadata leak, service disruption.

**Compromised Chutes.ai TEE provider** A global TEE provider compromise (e.g., stolen Intel key material) could allow an attacker to create fake attestations. This would affect all TEE customers globally, not just ZDrive. Mitigated by: multiple attestation nonces per request, on-chain attestation records for audit.

**Compromised Irys/Arweave upload path** An attacker who intercepts the Irys upload sees ciphertext only. They cannot modify the DataItem without invalidating the operator signature. They cannot decrypt the contents. Worst case: upload fails or is delayed; user retries.

**Wallet private key stolen** If your wallet key is compromised, an attacker can: derive your encryption key (and decrypt your vault), sign new auth sessions, spend your credits. This is equivalent to losing your password with no recovery mechanism — protect your wallet key accordingly.

**Session token replay** An attacker who intercepts a session token + wallet signature can replay it within the session window. The UUID binding limits this to the current session only. Mitigation: use HTTPS (enforced), rotate sessions regularly, use a hardware wallet.

## What is not guaranteed

* Worker operator honesty about log retention policies
* Uptime or availability of free tiers
* Correctness of inference outputs (a TEE guarantees isolation, not accuracy)
* Future contract upgrades (UUPS proxy means the contract logic can change — monitor upgrade events on-chain)


# API Reference

All API endpoints are served from the CF Worker. Production base URL: `https://app.zdrive.io`

## Authentication

All authenticated endpoints require these fields in the request body (or headers for vault upload):

| Field          | Type   | Description                            |
| -------------- | ------ | -------------------------------------- |
| `x_wallet`     | string | Wallet address (checksummed)           |
| `x_wallet_sig` | string | ERC-1271 signature of the auth message |
| `x_session`    | string | Session UUID (generated client-side)   |

If wallet auth is omitted or invalid, the request falls back to anonymous session-based access.

***

## POST /v1/chat/completions

Proxy to Chutes.ai TEE inference. Streams the response as SSE.

**Request body** (JSON):

```json
{
  "x_wallet": "0x...",
  "x_wallet_sig": "0x...",
  "x_session": "uuid-v4",
  "model": "deepseek-ai/DeepSeek-V3.1-TEE",
  "messages": [
    { "role": "user", "content": "Hello" }
  ],
  "stream": true
}
```

**Supported models:**

* `deepseek-ai/DeepSeek-V3.1-TEE` (paid)
* `deepseek-ai/DeepSeek-V3.2-TEE` (paid)
* `deepseek-ai/DeepSeek-R1-0528-TEE` (paid)
* `Qwen/Qwen3-32B-TEE` (free + paid)
* `Qwen/Qwen3-235B-A22B-Instruct-2507-TEE` (paid)
* `MiniMaxAI/MiniMax-M2.5-TEE` (free + paid)
* `openai/gpt-oss-120b-TEE` (paid)

**Response:** SSE stream (OpenAI-compatible format)

**Response headers:**

| Header                | Value                                                                               |
| --------------------- | ----------------------------------------------------------------------------------- |
| `X-Zdrive-Model-Used` | Actual model that processed the request (may differ from requested due to fallback) |

**Error codes:**

| Status | Meaning                                       |
| ------ | --------------------------------------------- |
| 401    | No identity (no wallet + no session)          |
| 403    | Datacenter ASN blocked                        |
| 413    | Input too long for tier                       |
| 429    | Rate limit reached                            |
| 503    | All fallback models unavailable / RPC failure |

***

## GET /v1/models

Returns which supported models are currently available on Chutes. Result is cached in KV for 5 minutes.

**Response:**

```json
{ "available": ["deepseek-ai/DeepSeek-V3.1-TEE", "Qwen/Qwen3-32B-TEE"] }
```

***

## GET /v1/attestation/report

Returns TEE attestation evidence for a given model.

**Query params:** `?model=deepseek-ai/DeepSeek-V3.1-TEE`

**Response:**

```json
{
  "gpu_tee_verified": true,
  "fpif_verified": true,
  "model_hash": "0x1a2b3c...",
  "gpu_arch": "BLACKWELL",
  "instance_count": 3,
  "failed_instances": 0
}
```

| Field              | Description                                              |
| ------------------ | -------------------------------------------------------- |
| `gpu_tee_verified` | GPU evidence present (confidential GPU confirmed)        |
| `fpif_verified`    | TDX quote present (Intel TDX enclave confirmed)          |
| `model_hash`       | SHA-256 of the TDX quote — fingerprint of running binary |
| `gpu_arch`         | GPU architecture (e.g. BLACKWELL, HOPPER)                |
| `instance_count`   | Number of live instances serving this model              |

***

## POST /vault/upload

Upload an AES-256-GCM encrypted blob to Arweave via Irys node2. Requires a connected wallet with credits > 0.

**Headers:**

| Header             | Required | Description                                |
| ------------------ | -------- | ------------------------------------------ |
| `x-wallet-address` | Yes      | Wallet address                             |
| `x-wallet-sig`     | Yes      | Auth signature                             |
| `x-wallet-session` | Yes      | Session UUID                               |
| `x-vault-tags`     | No       | JSON array of `{name, value}` Arweave tags |
| `Content-Type`     | Yes      | `application/octet-stream`                 |

**Body:** Raw binary ciphertext

**Response:**

```json
{ "id": "arweave-tx-id" }
```

**Error codes:**

| Status | Meaning                                     |
| ------ | ------------------------------------------- |
| 401    | Missing wallet auth headers                 |
| 402    | Credits required — purchase to unlock vault |
| 403    | Invalid wallet signature                    |
| 502    | Irys upload failed                          |

***

## GET /vault/storage-ready

Check if the operator's Irys node2 account has sufficient balance for uploads.

**Response:**

```json
{ "ready": true }
```

or

```json
{ "ready": false, "reason": "Irys account needs funding" }
```

***

## GET /health

Returns worker status.

**Response:**

```json
{
  "status": "ok",
  "inference_provider": "chutes",
  "inference_auth": "configured",
  "arweave_tx": "PLNL3s9T0...",
  "timestamp": "2026-05-17T12:00:00.000Z"
}
```


