Self-hosting
Every setting for Cloudflare, Docker and Neon, and how storage, previews and expiry work on each.
jalco-drop is one app with three entry points: src/worker.ts for Cloudflare, src/node.ts for Node and Docker, and src/neon-function.ts for Neon Functions. Storage, rate limiting, preview cards and the hourly cleanup sit behind small interfaces, so all three behave the same.
| Cloudflare | Docker | Neon | |
|---|---|---|---|
| Storage | R2 | A /data volume, or any S3-compatible bucket |
A Neon bucket |
| Preview cards | Browser Rendering | Chromium in the image | Summary card, no image |
| Rate limit | Workers rate limiting, 60 a minute per IP | In memory, 60 a minute per IP | In memory, per instance |
| Expiry cleanup | Hourly cron trigger | Hourly timer | Hourly schedule trigger |
| Largest upload | 100 MB (Cloudflare’s limit) | MAX_UPLOAD_MB, default 100 |
Neon’s request limit |
Cloudflare
Settings are vars in wrangler.local.jsonc, which setup writes from the wrangler.jsonc template. npm run dev and npm run deploy use it when it exists. The hostname is bound as a custom domain, so its zone must be on the same Cloudflare account. ADMIN_TOKEN is a secret:
npx wrangler secret put ADMIN_TOKEN -c wrangler.local.jsonc
npm run deploy
Docker
Settings are environment variables, usually in .env:
| Variable | Default | |
|---|---|---|
ADMIN_TOKEN |
required | Signs in to /admin and signs session, unlock and card cookies |
SITE_NAME, OWNER_NAME, ACCENT_COLOR, CODE_THEME, HOME_URL |
Starting settings | |
STORAGE |
disk |
disk or s3 |
DATA_DIR |
/data |
Where disk storage keeps files |
S3_ENDPOINT, S3_BUCKET, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY |
For s3: AWS S3, Backblaze B2, MinIO, or R2’s S3 API |
|
CHROME_PATH |
/usr/bin/chromium |
Browser for preview cards |
CARDS |
on | off skips preview cards |
MAX_UPLOAD_MB |
100 |
Largest upload |
TRUST_PROXY |
true |
Read X-Forwarded-* from your reverse proxy |
Run it behind a reverse proxy with TLS. Rotating ADMIN_TOKEN signs everyone out.
Neon
neon.ts is the whole deployment: the drop function (built to dist/neon by npm run build:neon, with the static files beside it), a private drops bucket, and a cleanup trigger that POSTs to /__scheduled every hour. The function only runs cleanup when the request carries Neon’s X-Neon-Trigger-Invocation-Id header, which Neon strips from outside callers.
npm run deploy:neon reads .env: ADMIN_TOKEN, the optional SITE_NAME, OWNER_NAME, ACCENT_COLOR, CODE_THEME and HOME_URL, and DROP_DOMAIN to serve from your own hostname. The bucket’s AWS_* credentials are injected by Neon.
npm run test:neon runs the smoke tests against the Neon build with any S3 endpoint in AWS_ENDPOINT_URL_S3.
Security
- Passwords are hashed with PBKDF2 and checked on the server. A locked drop never sends its bytes until it is unlocked.
- Raw files are served with a sandboxing Content-Security-Policy, so an uploaded HTML or SVG file cannot run script on your domain.
/redirects away, nothing is listable without a token, random slugs are 10 characters, and pages sendnoindex. Link-preview bots are allowed inrobots.txt; search crawlers are not.