Security Architecture
This page explains exactly how VaultLink works at a cryptographic level, what it protects against, and where its boundaries are. No marketing. If you're reviewing this with a security mindset — that's intentional.
TL;DR
All encryption and decryption happen in the browser using the Web Crypto API. The server stores only ciphertext and never receives the encryption key. The key travels only in the URL fragment, which is never sent in HTTP requests and is immediately stripped from history after reading.
Encryption Flow (sender, in-browser)
- Browser generates a random 256-bit key via
crypto.getRandomValues. - A unique 128-bit salt is generated.
PBKDF2(SHA-256, 100,000 iterations) derives an AES-256-GCM key.- Secret is encrypted with AES-256-GCM using a unique 96-bit IV.
- Only
{ encrypted_blob, iv, salt }are sent to the server.
The raw encryption key never leaves the browser.
Key Transport via URL Fragment
The key is appended to the access URL as a URL fragment:
/access?token=<access_token>#key=<base64_key>- ✓ URL fragments are never sent in HTTP requests.
- ✓ Do not appear in server logs, reverse proxies, or APM tools.
- ✓ Not included in the
Refererheader. - ✓
history.replaceStateremoves the fragment immediately after reading — it survives neither a Back press nor a copy of the address bar. - ✓ Key is held only in React component state — never written to
localStorage,sessionStorage, cookies, or global scope.
Decryption Flow (recipient, in-browser)
- Recipient verifies identity via 6-digit OTP sent to their email.
- Server returns only
encrypted_blob,iv, andsalt. - Browser reads the key from the URL fragment (already stripped from history).
- Decryption runs locally via Web Crypto API.
- Plaintext exists only in component memory and is cleared on navigation.
At no point does plaintext pass through the server, appear in server logs, or persist in server memory.
Data at Rest
The database stores:
encrypted_blobciphertext onlyiv + saltrequired for decryption, useless without the keylabel, note, recipient_emailplaintext — see Known Metadata belowOTP hashSHA-256, not reversibleIP addressesSHA-256(ip + IP_HASH_SALT), not reversible without the salt
IP_HASH_SALT.Atomic View Enforcement
View limits are enforced atomically in Postgres to prevent race condition bypass:
UPDATE secrets
SET view_count = view_count + 1
WHERE id = p_id
AND (p_max_views IS NULL OR view_count < p_max_views)
RETURNING TRUE;- ✓Prevents Race condition bypass
- ✓Prevents Double-consume attacks
- ✓Prevents TOCTOU flaws
OTP & Rate Limiting
- ✓6-digit OTP, SHA-256 hash stored in DB
- ✓Max 5 attempts, 10-minute expiry
- ✓Sliding-window rate limiter via Upstash Redis (distributed — shared across all server instances)
- ✓Prevents OTP brute-force and multi-instance rate limit bypass
Threat Boundaries
Protects against
- ✓Database compromise— ciphertext only
- ✓Passive network interception
- ✓Server log leakage of keys
- ✓OTP brute force
- ✓View race conditions
- ✓Secret tampering— AES-GCM auth tag
Does not protect against
- ✗Malicious recipient copying the secret
- ✗Screenshot or recording
- ✗Compromised recipient device
- ✗XSS from future code changes
- ✗Full runtime server compromise
Zero-knowledge with respect to storage — not zero-trust against recipients.
Known Plaintext Metadata
For usability, the following fields are stored unencrypted: label, note, recipient_email. Secret values themselves remain encrypted.
Sender Identity
Sender identity is derived from a random token stored in localStorage. This is a convenience feature — it allows the dashboard to group secrets by the browser that created them. It is not a secure identity mechanism, not authenticated, and not tied to any account or credential. Do not treat it as proof of authorship.
Design Philosophy
VaultLink is intentionally scoped. It is not a password manager, a vault replacement, a compliance-certified platform, or a screenshot prevention tool.
It is a pragmatic bridge between systems — built to reduce accidental credential exposure in chat-based workflows. Security decisions prioritize minimal server trust, explicit threat boundaries, atomic enforcement, and distributed rate limiting.
VaultLink is built to be boringly correct.