Skip to content
s3nd
Menu

Use case

End-to-end encrypted

Encrypt in the browser with a passphrase. Your server and your bucket store bytes they cannot read.

const key = await deriveKey(passphrase, salt) // PBKDF2 or Argon2, in the browser
const iv = crypto.getRandomValues(new Uint8Array(12))
const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoded)

// The transfer holds the salt, the IV and the ciphertext. Nothing else.
await transfers.createSnapshot({
  data: { salt: toBase64(salt), iv: toBase64(iv), ciphertext: toBase64(ciphertext) },
  version: 3,
})

The situation

What is actually going on.

The problem

By default your server can read every transfer it stores. For a journal, a password manager, health data or a contract, that is the wrong default, and a liability you may not want to hold.

What s3nd does about it

s3nd stores whatever you hand it. Derive a key from a passphrase with WebCrypto, encrypt the payload in the browser, and send the ciphertext. The bucket, your server and anyone who guesses the code see bytes they cannot use; the passphrase travels with the person, not over the wire. The same works for a file as for a snapshot.

Worth watching

The things that are easy to get wrong.

Compression happens after encryption

Ciphertext does not compress. Expect the stored size to match the raw size, and set maxSize accordingly.

Two secrets, two channels

The code goes over one channel and the passphrase over another, or in the person’s head. A code alone is now worth nothing to a guesser.

A lost passphrase is lost data

That is what end-to-end means. Say so in the interface before the user relies on it.

Packages@s3nd/protocols3nd

Put a file. Hand over the code.

Point it at the bucket you already pay for. Nothing to deploy, nothing to sign up for, nothing in the middle.

npm install -g @s3nd/clis3nd init --provider r2 --bucket drops3nd put ./anything.zip