The cloud backend for Screendrop, an open-source native macOS screenshot tool. This worker handles uploading, storing, and sharing screenshots and screen recordings via shareable links.
Built with Hono on Cloudflare Workers, using R2 for file storage and D1 for metadata.
- The Screendrop macOS app captures a screenshot or screen recording
- The file is uploaded to this worker (either via multipart form or streaming upload)
- The worker stores the file in R2 and creates a metadata row in D1
- A shareable link is returned (e.g.
https://your-worker.workers.dev/a1b2c3d4) - Anyone with the link sees a clean viewer page with download, copy link, and copy image actions
Click the button above to deploy to your Cloudflare account. The deploy flow will:
- Clone this repo into your GitHub account
- Automatically provision an R2 bucket and D1 database
- Prompt you to set an
UPLOAD_TOKENsecret (choose a secure token and save it — you'll need it in the Screendrop app) - Deploy the worker and set up CI/CD via Workers Builds
Once deployed, open the Screendrop app settings, go to the Cloud tab, and paste your worker URL and upload token.
If you prefer to deploy manually:
git clone https://github.com/fayazara/screendrop-worker.git
cd screendrop-worker
npm install
# Set your upload token as a secret
wrangler secret put UPLOAD_TOKEN
# Deploy (auto-provisions R2 + D1, then applies migrations)
npm run deployAll API routes are CORS-enabled. Routes marked with a lock require a Bearer token (UPLOAD_TOKEN).
| Method | Route | Auth | Description |
|---|---|---|---|
GET |
/api/ping |
Bearer | Connection health check |
POST |
/api/upload |
Bearer | Multipart file upload |
PUT |
/api/upload |
Bearer | Streaming upload (raw bytes, metadata via headers) |
POST |
/api/register |
Bearer | Register metadata for a file already uploaded to R2 |
GET |
/api/media/:id |
Public | Serve raw file from R2 |
GET |
/:id |
Public | Image/video viewer page with OG tags |
curl -X POST https://your-worker.workers.dev/api/upload \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@screenshot.png" \
-F "width=1920" \
-F "height=1080"For large files. The request body is the raw file — no buffering in Worker memory.
curl -X PUT https://your-worker.workers.dev/api/upload \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: image/png" \
-H "X-Filename: screenshot.png" \
-H "X-Width: 1920" \
-H "X-Height: 1080" \
--data-binary @screenshot.png{
"id": "a1b2c3d4",
"url": "https://your-worker.workers.dev/a1b2c3d4",
"filename": "screenshot.png",
"size": 204800
}Set via wrangler secret put, or prompted automatically during the Deploy to Cloudflare flow (defined in .dev.vars.example):
| Secret | Description | Required |
|---|---|---|
UPLOAD_TOKEN |
Shared token for authenticating uploads from the Screendrop app | Yes |
AUTHOR_NAME |
Display name shown on shared pages (falls back to Anonymous) |
No |
AUTHOR_AVATAR |
Avatar URL shown on shared pages (falls back to a generated avatar) | No |
| Type | Binding | Purpose |
|---|---|---|
| R2 | BUCKET |
File storage for screenshots and recordings |
| D1 | DB |
SQLite database for upload metadata |
npm install
npm run devThis starts a local dev server at http://localhost:5173 with hot reload. Local R2 and D1 resources are created automatically and persist between runs.
# Apply migrations locally
npm run db:migrate:local
# Apply migrations to production
npm run db:migrate:remoteMIT