How AdaptoSecret Works

You have a password, API key, or some other secret you need to send to someone. Email is risky. Slack logs everything. Text messages stick around forever.

AdaptoSecret gives you a safer way: paste your secret, get a link, send that link. When the recipient opens it, they see the secret once. Then it is gone. Not hidden, not archived — deleted from our servers entirely.

The zero-knowledge part

Here is the key thing: we never actually see your secret.

When you create a secret link, your browser generates a random encryption key and uses it to scramble your secret before anything leaves your device. That scrambled data is what we store. The key itself lives in the link, after the # symbol.

That #part of a URL is special: browsers never send it to servers. It stays entirely on your device and the recipient's device. So we receive the encrypted gibberish, but we never receive the key needed to read it.

All we can do is store encrypted data we cannot decrypt, and delete it when someone retrieves it.

Optional passphrase protection

For extra security, you can add a passphrase. The recipient will need to enter it before they can see the secret. This means even if someone intercepts the link, they still cannot read the secret without the passphrase you share separately.

The passphrase protection uses serious cryptography — the same kind used to protect cryptocurrency wallets. It is designed to make brute-force guessing impractical even with powerful hardware.

For Security Teams

Written for CISOs, security engineers, and IT leadership evaluating AdaptoSecret for organizational use. What follows is the security posture as actually shipped, not as aspirationally described.

Trust Model

The zero-knowledge architecture moves the trust boundary to the endpoints. Here is exactly what that means in practice:

Inside the trust boundary: the sender's and receiver's browsers, the TLS channel to Vercel's edge, and the JavaScript bundle we serve (pinned by SRI in M4). If any of these are compromised, the zero-knowledge property degrades.
Outside the trust boundary: our database (Neon), our rate-limit store (Upstash), our application logs, and any intermediate CDN cache. All three are explicitly untrusted for confidentiality and are architected to handle only encrypted material we cannot decrypt.

What a Full Database Compromise Exposes

If an attacker obtained complete read access to our Neon database, they would have:

  • Random 16-character record IDs
  • AES-GCM ciphertext with 96-bit IVs
  • Creation timestamps, expiration timestamps, view counters
  • For passphrase-protected records: wrapped keys, 128-bit Argon2id salts, KDF parameters

They would NOT have any plaintext, nor any key material capable of decrypting the ciphertext. Per-secret encryption keys live exclusively in URL fragments, which by HTTP specification are never transmitted to the server. There is no master key to steal because no master key exists.

For passphrase-protected secrets, an attacker with full database access plus possession of the share URL would still face an Argon2id brute-force against the user-chosen passphrase, parameterized at the OWASP 2023 minimum (m=19MB, t=2, p=1).

Compliance Framework Alignment

The architecture is mapped against five frameworks. These are alignment mappings, not attestations. Formal certification requires third-party audit engagement, which is planned pre-launch.

  • SOC 2 Type II: Common Criteria CC1 through CC9 mapped. Security, Availability, Processing Integrity, Confidentiality, and Privacy all addressed architecturally.
  • ISO 27001:2022: Annex A controls A.5.15, A.5.29, A.5.34, A.8.5, A.8.9, A.8.10, A.8.12, A.8.24, A.8.26 mapped to implementation.
  • HIPAA Technical Safeguards: 45 CFR 164.312 integrity and transmission security satisfied at the crypto layer. Access control and audit trail expand with the Phase 2 account system.
  • GDPR: Data minimization, storage limitation, and integrity/confidentiality satisfied by design. Right to erasure is automatic. Right of access to plaintext is architecturally impossible to fulfill, which is documented for data subject requests.
  • CCPA: Minimal personal information collected. Right to know returns minimal data. Right to delete is automatic.

Third-Party Security Audit

An independent cryptographic and application security audit is scoped as a pre-launch requirement. Candidate firms under evaluation include Cure53, Doyensec, and Trail of Bits. Scope covers the client-side cryptography, the zero-knowledge property, the atomic consume transaction, CSP and SRI configuration, and rate-limit bypass potential. Audit report will be published or shareable under NDA once complete.

Law Enforcement and Legal Process

In response to a valid subpoena, warrant, or other legal process, AdapToIT can produce:

  • Ciphertext and metadata for records not yet purged
  • Hashed IP prefix records from rate-limit logs (90-day retention)
  • Creation and consumption timestamps for a given record ID

We cannot produce plaintext because we have never possessed the decryption keys. This is an architectural property, not a policy choice, and it applies equally to lawful process, insider threat, and external compromise.

Vendor Security Posture

Our service depends on three managed vendors. Each maintains its own compliance program:

  • Vercel (hosting, edge, TLS): SOC 2 Type II, ISO 27001, HIPAA (Enterprise), PCI DSS
  • Neon (Postgres database for ciphertext storage): SOC 2 Type II, HIPAA (Business)
  • Upstash (Redis for rate-limit counters only, never secret content): SOC 2 Type II, GDPR

Data Retention

  • Ciphertext and crypto material: nulled atomically on the final authorized view. Present only during the valid-view window.
  • Metadata row (ID, timestamps, view counts): retained up to 24 hours past consumption or expiry, then hard-deleted by cron. No soft-delete, no archive table.
  • Rate-limit counters: 60-second TTL in Upstash.
  • Application logs: 90 days in Vercel log drain. Contain hashed record ID prefix and hashed IP prefix only. No URLs, no fragments, no plaintext, no ciphertext.
  • Database backups of the secrets table: explicitly disabled at the Neon role level. No point-in-time recovery for ciphertext.

Responsible Disclosure

Suspected vulnerabilities: please contact security@adaptoit.com. Do not open public GitHub issues for security matters. We aim to acknowledge reports within 2 business days.

The Tech Geek Stuff

Encryption

All encryption happens client-side using the Web Crypto API. No external crypto libraries for the core encryption path.

  • Algorithm: AES-GCM with 256-bit keys
  • IV: 96-bit random IV generated fresh for each encryption (GCM standard)
  • Key transport: Raw key exported to base64url, placed in URL fragment
  • Fragment behavior: Per RFC 3986, the fragment identifier is never sent to the server in HTTP requests

Passphrase Mode (Argon2id)

When passphrase protection is enabled, the URL key is wrapped with a key derived from the passphrase using Argon2id.

  • KDF: Argon2id via hash-wasm (WebAssembly implementation)
  • Parameters: m=19456 (19 MB), t=2, p=1 — OWASP minimum floor
  • Hash length: 256 bits (matches AES key size)
  • Salt: 128-bit random, stored with the secret
  • Memory hard-fail: Devices reporting navigator.deviceMemory < 0.5 GB are blocked from passphrase mode rather than silently degrading security

Server-Side Storage

  • Database: Neon (serverless Postgres)
  • Atomic consume: Single-query read-and-delete using DELETE ... RETURNING — no race conditions, no double-reads
  • Max views: Configurable (1, 3, or 10) — decremented atomically on each retrieve
  • Expiration: 1 hour, 24 hours, or 7 days from creation
  • Purge: Cron job runs every 15 minutes, hard-deletes all expired secrets (no soft-delete)
  • 24-hour grace purge: All secrets older than 24 hours past expiry are purged regardless of view state

Rate Limiting

Implemented via Upstash Redis with sliding window algorithm.

  • POST (create): 10 requests per minute per IP
  • GET (retrieve): 60 requests per minute per IP
  • IPv6 handling: Collapsed to /64 prefix (first four hextets) to prevent trivial rate limit bypass via address rotation
  • Response: 429 with Retry-After header

Security Headers

  • HSTS: max-age=63072000; includeSubDomains; preload
  • CSP: strict-dynamic with per-request nonces
  • Frame protection: frame-ancestors 'none' (CSP) + X-Frame-Options: DENY
  • Secret routes: /s/* and /api/secrets/* get extra-strict Referrer-Policy: no-referrer and Cache-Control: no-store

Accepted Tradeoffs

Two CSP relaxations are required by our current stack:

  • style-src 'unsafe-inline' — Tailwind CSS 4 uses @theme inline which injects styles at runtime. Until Tailwind supports nonce-based style injection, this relaxation is necessary. Style injection is significantly lower risk than script injection.
  • 'wasm-unsafe-eval' — Required for WebAssembly compilation of hash-wasm (Argon2id in passphrase mode). This directive permits WebAssembly.compile() and WebAssembly.instantiate() but does NOT re-enable JavaScript eval(). Browser support: Chrome 97+, Firefox 102+, Safari 15.4+.

Error Response Design

All "not found" conditions — never existed, already consumed, expired — return identical 404 responses with matching headers and body. This prevents attackers from distinguishing between valid consumed secrets and invalid IDs, eliminating an information disclosure vector.

AdaptoSecret is open source. Review the implementation at github.com/TheOtherChrisBrown/adaptosecret.