Skip to main content
This page is about FHE keys (the cryptographic objects that encrypt your data). For API keys (the auth tokens for api.wavis.xyz), see Authentication.

The three FHE keys

Every FHE session has three keys generated together as a triple:
keygen() → (secret_key, public_key, evaluation_key)
KeySizeWhere it livesWhat it does
Secret key~5 KBClient only — never sentDecrypts ciphertexts
Public key~2 MBAnywhereEncrypts plaintexts
Evaluation key~40 MBServerBootstraps gates on ciphertext
The secret key is the only one that breaks the security guarantee if leaked. Treat it like a bank password — generate fresh, store encrypted, never upload.

Generating keys (Python)

import wavis_fhe as wv

# Default preset (fast_128)
keys = wv.keygen()

# Explicit preset
keys = wv.keygen("standard_128")

# GPU-accelerated keygen (BYO GPU)
keys_gpu = wv.keygen_gpu(device=0)

# Inspect
print(keys.n())       # 636 — TLWE dimension
print(keys.big_n())   # 1024 — RLWE ring degree
Keygen takes ~5 seconds on CPU the first time (CPU-bound polynomial expansion). It’s deterministic given a seed — pass seed=... for reproducibility in tests.

Generating keys (REST — server-side, deprecated)

POST /api/v1/keys/generate will generate keys server-side. This is not recommended because it requires the server to briefly hold the secret key to derive the evaluation key. Prefer client-side keygen + /tfhe/eval-session upload (FHE-blind mode).
curl -X POST https://api.wavis.xyz/api/v1/keys/generate \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "scheme": "rns-ckks",
    "params": {
      "poly_degree": 8192,
      "scale_bits": 40,
      "security_level": "128-bit",
      "max_depth": 10
    }
  }'
The response includes secret_key only if WAVIS_ALLOW_SK_IN_RESPONSE=true on the server (off by default). Otherwise, the secret key is held in encrypted session storage and you can use it via session ID — but the cleanest pattern is to generate locally.

Parameter selection

For TFHE (Boolean gates), use a named preset — see Performance. For CKKS (arithmetic), the four parameters interact:
ParameterWhat it controlsTrade-off
poly_degreeRLWE ring size (slot count)Higher = more parallelism + more security, but slower
scale_bitsPrecision of fixed-point encodingHigher = more decimal places, but consumes noise budget faster
max_depthRNS modulus chain lengthHigher = deeper circuits without bootstrap, but bigger ciphertexts
security_levelBits of security”128-bit” is required for production
Recommended starting points:
Use casepoly_degreescale_bitsmax_depth
Demo / prototype4096305
ML inference (MLP, ≤5 layers)8192408
Small CNN163844012
Deep transformer327685014
Allowed poly_degree values are NIST PQC-validated: 64, 256, 4096, 8192, 16384, 32768. The smaller two (64, 256) are for testing only and don’t provide 128-bit security — the API returns X-Security-Warning if you use them with a non-testing security level.

Encryption

Once you have keys, encryption is local and instant:
ct = keys.encrypt(True)          # TFHE: encrypt a single bit
cts = keys.encrypt_batch([True, False, True])  # multiple bits in one call
For CKKS:
import wavis as wv
wv.api_key = "wvs_live_..."

session = wv.create_session(scheme="rns-ckks", poly_degree=8192)
encrypted = session.encrypt([1.0, 2.0, 3.14, 4.0])
Ciphertexts are ~2 KB each for TLWE (TFHE) and ~80–500 KB for CKKS depending on parameters.

Uploading the evaluation key (FHE-Blind)

import base64, requests

ek_bytes = keys.eval_key_bytes()
ek_b64 = base64.b64encode(ek_bytes).decode()

resp = requests.post(
    "https://api.wavis.xyz/api/v1/tfhe/eval-session",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={"eval_key_b64": ek_b64}
)
session_id = resp.json()["session_id"]
The evaluation key (~40 MB) is uploaded once per session and reused for all gates in that session. The server stores it encrypted at rest; it’s zeroed when the session expires.

Session lifecycle

PhaseDurationWhat happens
CreatedInstantSession ID returned, eval key indexed
ActiveUp to 1 h (default TTL)Gates evaluated, ciphertexts cached
IdleLast 5 min of TTLNo new ciphertexts cached
ExpiredAfter TTLEval key zeroed, ciphertexts evicted
Get session info:
GET /api/v1/tfhe/session/{session_id}
{
  "session_id": "sess_...",
  "preset": "standard_128",
  "gates_used": 1024,
  "total_cost_jpy": 102.4,
  "age_s": 1523,
  "is_blind": true
}

Deleting a session (immediate)

DELETE /api/v1/tfhe/session/{session_id}
Returns 204 No Content. The eval key is zeroed and all cached ciphertexts are deleted within 5 seconds. Always delete sessions when done — it frees server memory and ends billing for that key’s footprint.

Deleting an FHE key (CKKS workflow)

For the /keys/* endpoints (CKKS):
DELETE /api/v1/keys/{key_id}
{
  "key_id": "key_...",
  "status": "deleted"
}
This deletes:
  • The public key
  • The evaluation key
  • All ciphertexts associated with the key (~unlimited)
Deletion is irreversible. Cached ciphertexts can be re-uploaded with new keys, but you cannot recover the deleted key materials.

Key rotation strategy

For long-running products:
  1. Generate a fresh key per session — the simplest and safest pattern. Each user’s session has its own keypair.
  2. Or rotate at fixed intervals — generate new keys daily, transition users gradually.
  3. Avoid long-lived global keys — if a single secret key encrypts a year of data and is later compromised, that’s a year of plaintext exposure.
WAVIS does not enforce rotation, but the dashboard exposes per-key creation timestamps to make audits easy.

Storing the secret key client-side

The secret key is bytes — store it however your platform stores secrets:
  • Server-side service: environment variable, AWS KMS, GCP Secret Manager, HashiCorp Vault.
  • Browser app: the secret key shouldn’t live in browser storage. Either derive it from a passphrase + PBKDF2 each session, or use the WebAuthn credentials API to bind to a hardware-backed key.
  • Mobile app: iOS Keychain (kSecAttrAccessibleWhenUnlockedThisDeviceOnly) / Android Keystore.
# Save secret key to encrypted file
import json
with open("/secure/path/wavis.key", "wb") as f:
    f.write(keys.secret_key_bytes())

# Load later
with open("/secure/path/wavis.key", "rb") as f:
    keys = wv.keys_from_secret_bytes(f.read())
Always encrypt at rest. WAVIS does not provide key escrow — if you lose your secret key, the ciphertexts are unrecoverable.

Common mistakes

Uploading the secret key to the server. This defeats the entire model. WAVIS’s /keys/upload endpoint accepts only public_key and eval_key; it rejects requests containing a secret key. Reusing a key across users. If user A’s key is used to encrypt user B’s data, A can decrypt B’s data. Use one key per security boundary. Forgetting to delete sessions. Active sessions count against your session quota and consume server memory. Delete when done. Choosing weak parameters. poly_degree=64 produces fast but insecure-by-modern-standards keys. The API warns; production code should treat the warning as an error.

Next Steps

Server Mode Example

End-to-end FHE-blind workflow

Keys API Reference

All key endpoints with schemas