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.
We fundamentally redesigned how NovAI handles your data. We only store a one-way SHA-256 hash of your key:
| Data | Before | Now |
|---|---|---|
| Your API Key | Stored as plaintext | ✔ Only SHA-256 hash |
| Your Prompts | Never stored | ✔ Never stored |
| Model Responses | Never stored | ✔ Never stored |
| Conversation History | Never stored | ✔ Never stored |
| Verification Code | Closed source | ✔ Open source |
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
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
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.
| Data | Stored? | Purpose |
|---|---|---|
| SHA-256 hash of API key | ✔ | Identity verification |
| Token usage count | ✔ | Billing |
| Model name per request | ✔ | Routing |
| Your email | ✔ | Account recovery |
| Your original API key | ✘ | — |
| Your prompts / messages | ✘ | — |
| Model responses | ✘ | — |
| Conversation history | ✘ | — |
| File uploads or images | ✘ | — |
| Threat | Protection |
|---|---|
| Database breach | Only SHA-256 hashes — keys cannot be reversed |
| Man-in-the-middle | All connections use TLS encryption |
| Insider threat | No conversation data exists to leak |
| Government data request | We have no conversation data to hand over |
| Brute force attack | 64-char hex key = 2²&sup5;&sup6; possibilities |
We believe trust should be based on proof, not promises. Here is our complete authentication code:
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()
# 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
# 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
# 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.
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.
You can't reset it — you generate a new one. This is by design, like a crypto wallet seed phrase.
Generate a new key from your dashboard. The old key's hash is deactivated, and your balance transfers.
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