Parent: access-control
The visitor's own key is XSS-resistant on the browser side: a non-extractable AES-GCM CryptoKey lives in IndexedDB (JS can call encrypt/decrypt but never read its raw bytes), and the key's ciphertext envelope lives in localStorage. Both stores must be compromised to exfiltrate — dumping either alone is useless (app/src/lib/gate/byoai-vault.ts).
Pairs with the wire-side byoai-envelope (AES-GCM keyed by session_token, never stored server-side). End to end, the visitor's key is never persisted in plaintext anywhere.