In one sentence

Files we cannot read. Keys we never see. Audits we make easy.

Encrypted in your browser

Each file is encrypted client-side with AES-256-GCM. We receive only ciphertext.

Keys we never receive

The decryption key travels in the share URL fragment. By web standard, fragments never reach our servers.

EU data residency

Ciphertext stored in Amsterdam. UK and EU GDPR compliant. DPA available before signup.

How it works

The complete flow, from drag-and-drop to download.

A Nullsend transfer is four steps. Three of them happen in browsers — yours and your recipient's. The middle step is the only time our servers are involved, and at that point the data is already encrypted with a key we don't have. Click any step for the technical detail.

1. Key generation — in your browser, by your browser

When you drop a file into the upload area, the first thing the page does is generate a fresh 256-bit symmetric key using your browser's crypto.getRandomValues() call. This is a cryptographic random source backed by the operating system's entropy pool — the same source that generates TLS session keys for every HTTPS connection you make.

The key never leaves the browser. We have no network call that could carry it out, and no JavaScript path that sends it to our servers.

// Simplified — the actual code adds chunking, error handling, and IV management.
// In your browser, before any upload begins:
const fileKey = await crypto.subtle.generateKey({
  name: "AES-GCM",
  length: 256
}, true, ["encrypt", "decrypt"]);

// This key now exists only in JavaScript memory.
// It is not transmitted to Nullsend. Not now, not ever.
2. Encryption — chunked, authenticated, in-browser

We split each file into 4 MB chunks and encrypt them one at a time. Each chunk gets a fresh 96-bit initialisation vector (IV) generated the same way as the key. The chunks are encrypted with AES-256 in GCM mode (Galois/Counter Mode), which is the standard authenticated cipher used in TLS, SSH, and virtually every modern protocol that handles real data.

Why authenticated encryption matters: GCM doesn't just hide the plaintext; it also produces a 128-bit authentication tag with each chunk. When your recipient decrypts, the browser verifies the tag and refuses to produce output if the ciphertext was tampered with. An attacker who modified bytes on our storage layer would cause decryption to fail, not produce garbled output.

Filenames are encrypted with the same key into a separate metadata blob, so even the names of your files aren't visible to us.

// What each chunk encryption looks like.
// For each 4 MB chunk:
const iv = crypto.getRandomValues(new Uint8Array(12));

const ciphertext = await crypto.subtle.encrypt(
  { name: "AES-GCM", iv },
  fileKey,
  chunkPlaintext
);

// Upload ciphertext + iv to storage.
// Plaintext is discarded from memory.
3. Upload — direct to encrypted storage, never via our app servers

The encrypted chunks stream directly from your browser to S3-compatible object storage in Amsterdam, using presigned URLs that our backend issues but never sees the data of. We use multipart upload so that a 5 GB transfer doesn't have to start over if the network blips on chunk 12.

What our backend sees: the size of each ciphertext chunk, the order they arrive in, your tenant ID, when the transfer was created, when it should expire. What we don't see: any plaintext, the filename, the file key, your recipient's identity (only their email if you chose to use our delivery option).

Storage is in the European Union — specifically, our object storage provider's EU-Central region, in Amsterdam. Ciphertext doesn't leave the EEA.

4. Sharing — the key travels in the URL fragment, not via our server

The share URL we hand back looks like this:

https://acme.nullsend.io/d/a7b2c91d#k=jK4r...base64KeyHere...Qx9

The part after the # is the URL fragment. Fragments are a web standard that browsers never include in HTTP requests. When your recipient clicks the link, their browser asks our servers for the transfer at /d/a7b2c91d — but the #k=... part stays in the browser's address bar and is read directly by the page's JavaScript to perform decryption locally.

We log requests for transfer IDs. We never see the keys, because the keys are in fragments, and fragments don't travel over the network to us. This isn't a policy — it's how every browser since the 1990s has worked. Verify it: RFC 3986, section 3.5.

If a password was set on the transfer, the URL has #p=1 instead of the key, and the recipient is prompted for the password. The password is stretched with Argon2id (memory-hard, slow on purpose) into a wrapping key that unwraps a server-stored, encrypted copy of the file key. We still never see the password or the file key — only the encrypted form, which is useless without the password.

5. Expiry — files self-destruct on a schedule

Every transfer has an expiry timestamp. Our deletion worker runs every 15 minutes, finds expired transfers, and deletes the ciphertext objects from storage plus the metadata row from the database. The deletion is logged with a transfer ID and timestamp — no content, no recipient details, just an audit trail that the deletion happened.

Tenants can set the account-wide default expiry within their plan's ceiling (7, 30, or 90 days), and a sender can override it per transfer. There's also an option to force-expire one hour after first download — useful for genuinely one-time deliveries.

Cryptographic summary

The boring details, for the people who check.

Everything below is browser-native via the W3C WebCrypto API. We do not implement custom cryptography. We do not stack multiple algorithms hoping for defence in depth. We do the boring, well-reviewed thing and we do it the same way as every browser-based encrypted product you trust.

PurposeAlgorithmParameters
File encryptionAES-256-GCM96-bit random IV per chunk · 128-bit auth tag
Filename encryptionAES-256-GCMSame key as files · separate IV
Password derivationArgon2idm = 64 MB · t = 3 · p = 1
Random generationcrypto.getRandomValuesOS-backed CSPRNG via WebCrypto
Transport encryptionTLS 1.3Cloudflare-terminated · HSTS preload
What we protect, what's still on you

Architectural guarantees we make, and the things even strong encryption can't fix.

Strong encryption protects files in transit and at rest. It does not protect against everything. Being honest about that is part of being a security-first product — both sides of this list matter for keeping your data safe.

What Nullsend protects against

  • Nullsend reading your files, today or ever
  • Any AI model — ours or anyone else's — training on your data
  • Files being exposed if our database is compromised (ciphertext only)
  • Files being exposed if our object storage is compromised (ciphertext only)
  • Subpoenas or legal compulsion against Nullsend — we can hand over ciphertext and metadata, which are useless without the key
  • Tampering with stored ciphertext — GCM's auth tag catches it on decrypt
  • Files persisting beyond their expiry — automated deletion, audited

What you're still responsible for

  • The endpoints — if a sender or recipient's device is compromised, the plaintext is exposed there
  • The share link itself — anyone with the full URL (including the part after the #) can decrypt
  • Virus scanning — we cannot scan ciphertext, so recipients should scan files on download
  • Recipient verification — Nullsend doesn't know who you intended to send a file to, only what was delivered
  • Password strength — if you use the password option, a weak password makes Argon2id's defence weaker
Sub-processors

Every third party we use, and what they touch.

Nullsend runs on top of a small, considered set of infrastructure providers. None of them ever see plaintext file content. All are listed here, and changes are notified to active customers at least 30 days before they take effect.

Cloudflare
DNS, CDN, WAF, TLS termination for marketing site. Sees encrypted traffic and source IP addresses.
Global PoPs, EU residency
Backblaze B2
Object storage for transfer ciphertext. Sees only encrypted bytes — never plaintext, never the key.
EU-Central (Amsterdam)
Stripe
Payment processing. Sees billing address, payment method. Never touches transfer content.
EU / UK
Postmark
Transactional email for the Nullsend-managed delivery option. Sees recipient email address and email body (not file content).
EU region
Anthropic
AI onboarding assistant during signup only. Sees onboarding conversation text — never file content, never recipient data.
Per Anthropic ZDR

A full, machine-readable sub-processor list is at /subprocessors and is the source of truth.

Compliance posture

What we comply with today, and what we're working towards.

We're honest about which frameworks we currently satisfy and which we're building towards. "Compliant" without a date or a status is meaningless; we'd rather tell you exactly where we are.

Live
UK GDPR

Full compliance. DPA available to all tenants pre-signup.

Live
EU GDPR

Full compliance. Data resident in the EEA. Standard Contractual Clauses included where applicable.

Live
UK Data Protection Act 2018

Same regulatory footprint as UK GDPR. ICO breach notification within 72 hours per Article 33.

Roadmap · Q4 2026
SOC 2 Type I

Control logging in place from day one to support audit. Type II to follow Q2 2027.

Future
HIPAA-aligned architecture

For US expansion. BAAs on Enterprise once architectural controls audited.

Under review
ISO 27001

Evaluating timing alongside SOC 2 Type II.

Operational security

The unsexy but real operating practices.

Architectural encryption is only half of operational security. Here's how Nullsend runs as a business.

Production access

All production system access requires hardware MFA (Yubikey or equivalent). No SSH passwords, no static credentials in code or environment files. Database access is via short-lived tokens scoped to specific operations. Production data is never accessible to support staff for debugging — when troubleshooting requires data access, it goes through a tenant-consented session token that's logged, time-limited, and auditable.

Key rotation and storage

The server-side keys we use for non-file metadata (tenant email lists, SMTP credentials) are rotated quarterly. They're stored in a dedicated key management service, not in the application database. Per-tenant keys are derived using HKDF from a master key plus tenant UUID, so a tenant-level key compromise doesn't extend to other tenants.

We don't store, rotate, or escrow per-transfer file keys — we never have them in the first place. There's nothing to rotate.

Penetration testing

Before public launch, Nullsend will commission an independent web application penetration test from a UK or EU-based testing firm (chosen so data accessed during testing stays in the EEA). The summary report will be available to Enterprise customers under NDA.

Incident response and breach notification

Nullsend maintains a documented incident response plan covering detection, containment, eradication, recovery, and notification. In the event of a personal data breach, notification to the relevant supervisory authority (ICO for UK customers, the lead supervisory authority for EU customers) is sent within 72 hours per GDPR Article 33. Affected tenants are notified directly within the same window.

Backups and data resilience

Transfer ciphertext is stored in multi-AZ object storage with built-in durability of 99.999999999% per object (eleven nines). Metadata is backed up nightly and retained for 30 days. We do not back up plaintext anywhere, ever — there is no plaintext to back up.

Get in touch

Reporting security issues, asking detailed questions.

Security issues, responsible disclosure

If you believe you've found a security vulnerability in Nullsend, please email hello@nullsend.io with the subject line beginning [security]. We'll acknowledge receipt within 24 hours and provide an initial response within 72 hours.

We're moving to a dedicated security@nullsend.io inbox and publishing a /.well-known/security.txt ahead of public launch. A formal bug bounty program is on the roadmap for v1.2.

Compliance and procurement questions

For DPA copies, SCC questions, sub-processor disclosures, or vendor onboarding documentation, contact hello@nullsend.io. We aim to respond to procurement requests within one business day.

Architectural questions

For deeper technical detail than this page covers — specific algorithm choices, why we picked X over Y, how a particular threat scenario is handled — write to hello@nullsend.io. We'd genuinely rather answer the question than have you guess.