wink.

Security & Threat Model

Last updated 2026-10-05 • Contact: paymentsonwink@gmail.com (subject: Security) • Chain id 4217 Tempo mainnet (not 42431 testnet)

Current model — honest (what we ship today)

Wink Wallet keys are encrypted on our server so same email restores same address. We can decrypt them.

This is custodial-adjacent, not fully non-custodial. Connected wallets (MetaMask, Phantom, Tonkeeper) are actually non-custodial — we only get address via EIP-1193, never private key. Until Q1 2027, default for new users should be connect MetaMask/Phantom, with Wink Wallet labeled recoverable by us.

How it works today:
1. Key generated in browser via crypto.getRandomValues (CSPRNG), stored in localStorage
2. Browser sends private key in plaintext to /api/wallet/primary/set over HTTPS → server encrypts with ENCRYPTION_KEY (server-side) → stores in user_primary_wallets.encryptedPrivateKey
3. Login on new device with same email → server decrypts with ENCRYPTION_KEY → returns same address, signs via /api/wallet/primary
4. Lose email + no exported seed = funds gone. Stolen inbox = stolen balance (attacker can login and we will decrypt for them).

What we store: Ciphertext + iv in DB. We hold ENCRYPTION_KEY to decrypt. Key travels to server before encryption.
What we cannot do today: We cannot prevent email takeover. We can decrypt if we have DB + ENCRYPTION_KEY.
What happens if DB leaks: Attacker gets ciphertext, needs ENCRYPTION_KEY to decrypt. If they get both DB + key, they can decrypt.
What happens if API logs / proxy / compromised route leaks: Key is sent to server before encryption — a server log, a proxy, or a compromised /api/wallet/primary/set route is a key leak, not just a DB leak. We mitigate with no plaintext logs, HTTPS only, short-lived memory, but the risk is real until client-side encryption ships. That's why we say we can decrypt and why default should be MetaMask/Phantom.
Mitigation Q1 2027: Client-side scrypt where server never sees key or passphrase — browser encrypts, uploads only salt+iv+ciphertext, we cannot decrypt even with DB + key.

Generation: WebCrypto CSPRNG in browser
Transport: Plaintext private key → /api/wallet/primary/set over HTTPS (current risk) → server encrypts
Storage: localStorage + encrypted blob in DB (user_primary_wallets.encryptedPrivateKey)
Encryption: Server ENCRYPTION_KEY (AES) — server can decrypt (current)
Sync: Same email = same address, auto-restore
Threat: DB + ENCRYPTION_KEY leak = decrypt possible. Server log/proxy/compromised route = key leak (key sent before encryption). Email takeover = balance takeover.
Connected wallets: Only address, never key — actually non-custodial, recommended default
Roadmap Q1 2027: Client-side scrypt, server never sees key, we cannot decrypt

1. What we can see vs what we cannot (current)

We CAN see / decrypt
  • Handle → Tempo address mapping (on-chain, public)
  • Payment links, amount, chain of origin (DB for dashboard)
  • Tempo tx hashes (public explorer 4217)
  • Wink Wallet encrypted blob + we hold ENCRYPTION_KEY to decrypt (current) — custodial-adjacent
  • Bridge quotes via Relay API
We CANNOT see
  • Connected wallet private keys (MetaMask/Phantom/Tonkeeper) — only address via EIP-1193
  • Your email password — only OTP via Resend
  • Future client-encrypted backup passphrase — server will never receive it (Q1 2027 roadmap)

2. Roadmap Q1 2027 — client-encrypted backup (you own password)

For PC + phone auto-restore without us being able to decrypt, we will ship client-side scrypt — NOT shipped yet, so do not claim "you own password" today.

1. Key generated in browser (CSPRNG)
2. User sets backup passphrase (you alone know, no email reset)
3. Browser: scrypt(passphrase, random salt N=32768 r=8 p=1) → key → AES-GCM-256 encrypt(privateKey, iv)
4. Upload ONLY: salt + iv + ciphertext + KDF params — never passphrase, never plaintext
5. New device: download ciphertext, enter same passphrase in browser, decrypt locally — server never sees passphrase
6. DB leak = only ciphertext, needs brute-force of your passphrase. No email reset possible.

Copy will THEN be: "Key encrypted in your browser with passphrase we cannot reset. We store only ciphertext."

Current server-side ENCRYPTION_KEY model is being replaced in Q1 2027. Until then, current model above is truth.

3. Handle registry — the real custody risk

@handle → Tempo address mapping on chain 4217 (not 42431 testnet). If we can change @handle's address, we can redirect payments. We lock it:

  • Registry contract on Tempo 4217 mainnet (address to be published, verified source on explorer)
  • Only current owner key can update mapping — requires signature from owner private key
  • 24h timelock on updates — users can see pending change and cancel
  • We cannot redirect without owner signature. Documented in architecture note.

4. What is audited?

ComponentAudited?Notes
Tempo L1 + pathUSD 0x20c0…0001 (4217)YesTempo team, mainnet 18 Mar 2026
OUSD 0x20c0…874beYesOpenStandard, $400M
Relay Protocol (13 chains)YesTrail of Bits — we use 13 direct routes, not 54
WinkPay custom contract holding fundsN/AWe have none — solver fills, non-custodial bridge
Handle registry + payrollPlanned Q1 2027OtterSec/Zellic at $10k volume
Wink Wallet encryption (current server-side)NoCustodial-adjacent, moving to client scrypt Q1 2027

5. User-facing copy — use this everywhere (send, claim, wallet, homepage badge, docs)

Wink Wallet keys are encrypted on our server so same email restores same address. We can decrypt them. Connected wallets like MetaMask are actually non-custodial. Lose email and do not export seed, funds gone.

Use this exact sentence in /send, /claim, /wallet, homepage badge, /docs. Do not say "keys never leave browser" or "you own password" until Q1 2027 client-side model ships. Do not say "non-custodial" for Wink Wallet today — say "encrypted backup, custodial-adjacent, see /security".
Questions? paymentsonwink@gmail.com • Chain 4217 Tempo mainnet (launched 18 Mar 2026, not 29 Sep 2025) • Stats: /stats shows live DB, 0 is honest, not dash • Docs proof tx is internal test, excluded from product stats to avoid curation