← Back to NovAI

Privacy-First: We Never Store Your API Keys or Conversations

March 17, 2026  |  PrivacySecurityOpen Source

TL;DR

Starting today, NovAI uses SHA-256 hashed key storage. We never store your original API key or any conversation data. Our authentication and proxy code is now open-source so you can verify this yourself.

What Changed

We fundamentally redesigned how NovAI handles your data. We only store a one-way SHA-256 hash of your key:

DataBeforeNow
Your API KeyStored as plaintext Only SHA-256 hash
Your PromptsNever stored Never stored
Model ResponsesNever stored Never stored
Conversation HistoryNever stored Never stored
Verification CodeClosed source Open source

How It Works

You  —(TLS)—>  NovAI  —(TLS)—>  Chinese AI Model
                       |
            hash key ✔ → deduct tokens
               that's ALL we do

Key Generation

When you register, we generate a cryptographically secure random key, show it to you once, then hash it with SHA-256 and store only the hash. The original key is discarded forever.

# What happens when you get a key:
full_key = "nvai-" + random_64_hex_chars     # shown to you ONCE
key_hash = SHA256(full_key)                   # stored in database
# full_key is DISCARDED from server forever

Request Verification

On every API request, we hash the key you send, compare it against stored hashes, then forward your request transparently to the upstream model.

# On every API request:
incoming_hash = SHA256(your_key_from_header)
if incoming_hash in database:
    proxy_request_to_upstream()    # forward as-is
    deduct_tokens(incoming_hash)   # bill by hash

⚠️ Important: Save Your API Key Securely

Your API key is shown only once when generated. After that, it cannot be viewed again — because we don't store it.

If you lose your key, we cannot recover it. You will need to generate a new one.

We recommend: save your key in a password manager, keep a local backup, and avoid over-charging your balance.

What We Store vs. What We Don't

DataStored?Purpose
SHA-256 hash of API keyIdentity verification
Token usage countBilling
Model name per requestRouting
Your emailAccount recovery
Your original API key
Your prompts / messages
Model responses
Conversation history
File uploads or images

Security Model

ThreatProtection
Database breachOnly SHA-256 hashes — keys cannot be reversed
Man-in-the-middleAll connections use TLS encryption
Insider threatNo conversation data exists to leak
Government data requestWe have no conversation data to hand over
Brute force attack64-char hex key = 2²&sup5;&sup6; possibilities

Open Source Code

We believe trust should be based on proof, not promises. Here is our complete authentication code:

auth.py — Key Hashing & Verification

import hashlib
from sqlalchemy.orm import Session
from app.models import User

def hash_api_key(key: str) -> str:
    """One-way SHA-256 hash. Cannot be reversed."""
    return hashlib.sha256(key.encode("utf-8")).hexdigest()

def get_user_by_key_hash(api_key: str, db: Session):
    """
    Verify API key by hash comparison.
    The raw api_key is hashed in memory, NEVER stored or logged.
    """
    key_hash = hash_api_key(api_key)
    return db.query(User).filter(
        User.key_hash == key_hash,
        User.is_active == True
    ).first()

proxy endpoint — Transparent Forwarding

# POST /v1/chat/completions
async def chat_completions(request: Request, db: Session):
    # 1. Extract key from header
    api_key = request.headers["Authorization"].replace("Bearer ", "")
    
    # 2. Hash and verify (key is NEVER stored)
    user = get_user_by_key_hash(api_key, db)
    if not user: raise HTTPException(401)
    
    # 3. Forward request AS-IS to upstream model
    # We do NOT read, log, or store the messages
    response = await forward_to_upstream(request.body)
    
    # 4. Extract ONLY token count for billing
    tokens = response.usage.total_tokens
    deduct_balance(user.key_hash, tokens)
    
    # 5. Return response to user - nothing saved
    return response

Database Schema

# What we store:
class User(Base):
    key_hash  = Column(String(64))   # SHA-256 hash only
    email     = Column(String(255))  # for account recovery
    balance   = Column(Float)        # remaining credits
    is_active = Column(Boolean)

class UsageLog(Base):
    key_hash     = Column(String(64))  # billed by hash
    model        = Column(String(64))  # which model
    tokens_used  = Column(Integer)     # for billing only
    
# What we DON'T store:
# - Your original API key
# - Your prompts or messages  
# - Model responses
# - Conversation history
# - System prompts
# - Any request/response content

Privacy Policy

# Formal privacy guarantees:
WHAT_WE_NEVER_STORE = [
    "Your original API key (only the hash)",
    "Your prompts or messages",
    "Model responses or completions",
    "Conversation history of any kind",
    "System prompts or tool definitions",
    "File uploads or images",
    "IP addresses (disabled by default)",
]

# Architecture:
# User --(TLS)--> NovAI --(TLS)--> Chinese AI Model
# We are a transparent pipe, not a data collector.

Verify It Yourself

Read the code above. Every function is documented. Every data flow is explicit. No hidden logging.

Get Your API Key →

Read it. Audit it. Trust it.

FAQ

If you don't store my key, how do I reset it?

You can't reset it — you generate a new one. This is by design, like a crypto wallet seed phrase.

What if I lose my key?

Generate a new key from your dashboard. The old key's hash is deactivated, and your balance transfers.

Why not end-to-end encryption?

We need to read the request body to route it to the correct upstream model. But we never persist it — it exists in memory only for the duration of the request.

NovAI — The privacy-first bridge between you and Chinese AI models.
Homepage · API Docs · Responses API