Storage provider
s3nd with Cloudflare R2
No egress fees, which is exactly what moving a 2 GB file between two laptops wants.
import { createBucket } from 's3nd'
const store = createBucket({
bucket: 'transfers',
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
publicUrl: 'https://cdn.example.com', // optional: your R2 custom domain
})Why this one
Cloudflare R2
R2 speaks the S3 API, ignores the region, and charges nothing for egress. That makes it the provider the "without a server" guide is written against: a bucket, a token scoped to it, one configuration file, and a code carried between two machines for a bill of nothing.
From the command line
A starter configuration, and what is left to do.
init writes the file with ${VAR} references rather than secrets, so it is meant to be committed; the env file it points at is not.
s3nd init --provider r2 --bucket transfers{
"bucket": "transfers",
"region": "auto",
"endpoint": "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com",
"prefix": "transfers",
"expiresIn": "24h",
"envFile": ".env",
"credentials": {
"accessKeyId": "${R2_ACCESS_KEY_ID}",
"secretAccessKey": "${R2_SECRET_ACCESS_KEY}"
}
}Then
- 01Put R2_ACCOUNT_ID, R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY in .env, and keep it out of git.
- 02The API token needs Object Read & Write on this bucket, and nothing else.
- 03Add a lifecycle rule that deletes objects under transfers/ after a day or two.
- 04Run
s3nd doctor. It performs the operations s3nd needs and reports what happened.
Worth knowing
Notes on R2.
region: "auto"
R2 ignores the region and the SDK insists on one. Setting an endpoint makes s3nd default to "auto", so there is nothing to write.
Conditional writes work
R2 implements If-None-Match and If-Match on PutObject, which is what keeps two simultaneous transfers from overwriting each other.
Expiry cleanup
expiresIn stops a transfer being handed over; only a lifecycle rule deletes the object. Give the prefix a rule that expires objects after a day or two, and s3nd doctor turns green.
Other providers
The endpoint is the only difference.
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