Version 1.0 — 2026-05-18 Author: Vaibhav Iyer (Founder, Veqtrx) Contact: contact@veqtrx.com
Veqtrx is a UK affordability-assessment platform for consumer credit and debt advice. This whitepaper documents how Veqtrx handles, encrypts, and protects customer data. It is intended for bank, lender, and regulator due-diligence reviews.
Veqtrx is built around a single technical commitment: the server cannot read customer data, even with full database access. This is enforced by client-side envelope encryption rather than by policy.
Verifiability: the cryptographic code is public at
github.com/vaibhavkiyer24/veqtrx-crypto-reference under MIT
licence. Banks and security researchers can read, test, and diff it
against what runs in production. No “trust us” required — verify
yourself.
Compliance roadmap (summary):
| Standard | Target | Notes |
|---|---|---|
| UK GDPR | ✅ Compliant at launch | Privacy policy + Vanish Protocol (Article 17) live |
| ICO registration | Q2 2026 | In progress |
| Cyber Essentials | Q3 2026 | Self-assessment phase |
| Cyber Essentials Plus | Q4 2026 | After first external audit |
| SOC 2 Type I | Q1 2027 | Target for first paying B2B partner |
| SOC 2 Type II | Q3 2027 | Target for first regulated lender partner |
| FCA authorisation | TBD | Only if regulated credit decisions become in-scope |
┌─────────────────────────────────────────────────────────────────────────┐
│ BROWSER (trusted client) │
│ │
│ User password ──► PBKDF2-SHA256 (600,000 iter, 32-byte salt) │
│ │ │
│ ▼ │
│ KEK (256-bit AES key, non-extractable) │
│ │
│ Per-budget data: │
│ ┌──────────────┐ ┌──────────────────────┐ │
│ │ Plaintext │ ───► │ AES-GCM encrypt │ ────► ciphertext│
│ │ budget JSON │ │ (random nonce) │ │
│ └──────────────┘ └──────────────────────┘ │
│ ▲ │
│ │ │
│ fresh DEK │
│ (256-bit) │
│ │ │
│ wrap DEK 3 ways for access control: │
│ │
│ AES-GCM(KEK) RSA-OAEP(agent pubkey) AES-GCM │
│ = customer wrap = agent wrap (share │
│ key) │
│ = creditor│
│ wrap │
│ │
└─────────┬──────────────────────────┬──────────────────────────┬─────────┘
│ ciphertext │ wrapped DEKs │
│ (opaque to server) │ (opaque to server) │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ SERVER (untrusted) — Render │
│ │
│ PostgreSQL: │
│ • customers (email, salt, kek_iter — never the password) │
│ • budget_reports (encrypted_blob, integrity_hash, metadata) │
│ • wrapped_keys (DEK wrapped for each principal) │
│ • identities (joined to customer via customer_id; encrypted) │
│ │
│ What the server never has: │
│ ❌ Plaintext password │
│ ❌ KEK │
│ ❌ DEK (only wrapped versions) │
│ ❌ Decrypted budget JSON │
│ ❌ Agent's private RSA key (only wrapped under agent KEK) │
│ ❌ Creditor share token (only the wrapped DEK) │
└─────────────────────────────────────────────────────────────────────────┘
| Primitive | Choice | Rationale |
|---|---|---|
| Password hashing for KEK | PBKDF2-SHA256, 600,000 iterations | OWASP 2026 recommendation. Argon2 is stronger but not yet in Web Crypto. |
| KEK, DEK | AES-GCM 256-bit | NIST-approved authenticated encryption. AEAD prevents ciphertext tampering. |
| Agent asymmetric | RSA-OAEP 2048-bit + SHA-256 | Only widely-available asymmetric option in Web Crypto. Curve25519 deferred. |
| Salt | 32 random bytes (crypto.getRandomValues) |
Exceeds NIST SP 800-132 minimum of 16. Stored alongside encrypted blob. |
| Share token (creditor) | 32 random bytes, base64url, in URL fragment | 256 bits of entropy. No KDF needed. URL fragment never sent to server. |
| TLS | TLS 1.3 (provider-managed) | Render + Netlify edge. HSTS enabled. |
| Database-at-rest | AES-256 (provider-managed) | Render Postgres encrypted volumes. Our own encryption layer is independent. |
All client-side cryptography uses the browser’s Web Crypto API
(crypto.subtle). No third-party crypto libraries
are imported. The audit surface is exactly one file
(src/zero-knowledge.ts, ~360 lines) plus the host browser’s
implementation.
| Key | Where it lives | When it exists | What guards it |
|---|---|---|---|
| Customer password | Browser memory only | User types it at login; discarded after KEK derivation | User’s responsibility (we recommend strong passwords) |
| Customer KEK | Browser memory only | Lifetime of the logged-in tab | Non-extractable CryptoKey (PBKDF2-derived) |
| Per-budget DEK | Browser memory; ciphertext wraps on server | Generated at budget save; reconstructed at decrypt | KEK (customer), RSA private key (agent), share token (creditor) |
| Agent RSA private key | Browser memory; wrapped on server under agent KEK | Lifetime of logged-in agent tab | Agent KEK (PBKDF2 from agent password) |
| Creditor share token | URL fragment (#...) on share link |
Browser memory of anyone holding the URL | URL secrecy + revocation = delete server-side wrapped DEK row |
| Admin token (operational) | Server env var + bcrypt hash in DB | Server lifetime | Render env var (set out-of-band; not in git) |
Critically: there is no server-side master key, KMS key, HSM key, or admin override that can decrypt customer data. If every Veqtrx employee, every server, and every backup were compromised simultaneously, customer financial data would remain ciphertext.
structlog,
routed to Render’s log aggregation. PII scrubbing applied at the SDK
level — Plaid errors are the only known leak vector and are flagged in
our security audit.persistence: 'memory'). No PII transmitted —
only event names + scrubbed properties (e.g. email_domain,
never the email).| Threat | Defence |
|---|---|
| Server-side data breach | Encrypted blobs are opaque. Attacker who exfiltrates the entire
budget_reports table gets ciphertext + integrity hashes. No
useable customer data. |
| TLS interception by malicious proxy | TLS 1.3 + HSTS. The actual data on the wire is already ciphertext from the browser; even a downgrade attack yields nothing useful. |
| Insider access at Veqtrx | No employee path to plaintext. Engineers cannot click through to a decrypted budget — the database admin panel can only show metadata + blob hashes. |
| Government subpoena for cleartext data | We can produce ciphertext but cannot decrypt it. Lawful-access requests for plaintext are technically impossible to fulfil. |
| Stolen admin credentials | Admin can edit metadata, suspend accounts, see analytics — but cannot decrypt financial data. Admin SQL console exists (audited tool); IP allowlist post-launch. |
| Replay / tampering of encrypted blobs | AES-GCM AEAD detects tampering. Plus an SHA-256 integrity hash of the original plaintext is stored separately for end-to-end verification. |
| Cross-tenant data leakage in partner API | Every assessment lookup is scoped to partner.id.
Cross-tenant returns 404 (not 403) to prevent enumeration. |
| CSRF on the customer flow | JWT in Authorization header, not cookies.
allow_credentials: false in CORS. CSRF not exploitable in
this design. |
| Brute force on customer password | PBKDF2 600k iter ≈ 600-900 ms per attempt on a fast device. Rate-limited 5/min per IP at the login endpoint. |
| Threat | Why not | Mitigation if it matters to you |
|---|---|---|
| Client device compromise | Malware in the user’s browser can read decrypted data while they view it. Same as every web app. | Customer-side endpoint security is the user’s responsibility. |
| Weak customer passwords | A user choosing password123 defeats PBKDF2. We require
≥8 chars + show a strength meter, but cannot force higher. |
Future: server-side password policy + breach-check via HIBP. |
| Phishing | A user typing their password into a fake site reveals it. | Standard browser security applies. We don’t currently offer hardware-key MFA. |
| Web Crypto bugs | We depend on crypto.subtle being implemented correctly.
We have no way to verify the browser’s implementation from JS. |
Trust shifts to the browser vendor. Same as every web app using crypto. |
| Bugs in our own crypto code | The code is published (veqtrx-crypto repo). Independent
review welcomed. |
Bug bounty programme planned post-launch. |
| Timing attacks on login | Our login endpoint may leak the existence of an email via response-time difference. | Flagged in pre-launch audit; constant-time comparison planned. |
| Sophisticated nation-state actors | We are not (yet) designed against $1M+ targeted attacks. | Out of scope for an early-stage product; reconsidered at scale. |
| Event | Data captured | Retention |
|---|---|---|
| Customer login | timestamp, IP country, user-agent (not full IP) | 90 days |
| Budget save | timestamp, blob hash, customer_id, principals granted | 90 days |
| Budget access by agent | timestamp, agent_id, principal route, ref_id | 365 days |
| Budget access by creditor | timestamp, IP country, ref_id, share token prefix (not full token) | 365 days |
| Admin action | timestamp, admin email, endpoint, JSON request body | 730 days |
| Vanish Protocol (GDPR Article 17) | timestamp, customer_id, what was scrubbed | Indefinite (proof of erasure) |
Logs are structured JSON (structlog), aggregated via
Render’s log pipeline. PII-scrubbed at SDK level.
For a partner / regulator wanting to confirm we accessed only what they expect:
agent_access_logs,
admin_actions, creditor_views.We commit to producing this export within 5 business days of a written request from a contracted partner.
For B2B partners on the Architecture B (white-label portal) tier: - A
webhook fires on every customer completion, providing real-time access
notification. - Quarterly the partner can request a CSV of all access
events scoped to their partner_id. - For incident response,
an emergency log export is available within 4 hours.
For B2B partners on Architecture A (direct API): - The same webhooks and quarterly CSV exports apply. - API call logs are scoped to the partner’s API key — they see only their own usage.
veqtrx-crypto repo by a named third party (NCC Group /
Trail of Bits / Cure53 shortlist).| Date | Activity | Vendor | Scope |
|---|---|---|---|
| Q3 2026 | Pen test #1 | TBD (shortlist: NCC, ThreatScout, F-Secure) | Application + crypto repo |
| Q4 2026 | Cyber Essentials Plus | UK CE accredited body | Infrastructure |
| Q1 2027 | SOC 2 Type I gap assessment | TBD | Customer-data handling controls |
| Q2 2027 | Independent crypto review | TBD | veqtrx-crypto repo + production diff |
| Q3 2027 | SOC 2 Type II audit period begins | Same auditor as Type I | 6-month observation window |
This document will be updated after each milestone.
| Term | Meaning |
|---|---|
| ZK | Zero-knowledge — server cannot read customer data |
| KEK | Key Encryption Key — derived from password, wraps DEKs |
| DEK | Data Encryption Key — per-budget AES-GCM key, wraps the actual data |
| PBKDF2 | Password-Based Key Derivation Function 2 — slow KDF resistant to brute force |
| AEAD | Authenticated Encryption with Associated Data — encryption that also detects tampering |
| AES-GCM | Galois/Counter Mode — NIST-standard AEAD construction |
| RSA-OAEP | Optimal Asymmetric Encryption Padding — RSA with secure padding |
| JWK | JSON Web Key — standard format for public-key transport |
crypto.subtle referenceThis document is published verbatim at
docs/security-whitepaper.md in the application repo. To
convert to PDF for distribution:
pandoc security-whitepaper.md -o security-whitepaper.pdf --pdf-engine=xelatex.