Fast Catbox-like hotlink file share.
Juicebox is a lightweight, high-speed file hosting service with direct hotlinking. Upload, share, done.
Prerequisites:
- Rust
- Node.js 20+ (for frontend bundle and Jest tests)
Install:
git clone https://github.com/create-juicey-app/juicebox.git
cd juicebox
cp .env.example .env
# Generate a secret (min 16 bytes) and add IP_HASH_SECRET to .env
openssl rand -hex 32Run:
cargo run --releaseCommon options (set in .env or your environment):
- MAILGUN_API_KEY - for reports, mailgun is the service of choice
- MAILGUN_DOMAIN - its domain for sending email (e.g. mail.juicey.dev)
- REPORT_EMAIL_TO - reciever's email for reports (e.g. admin@juicey.dev)
- REPORT_EMAIL_FROM - domain user (e.g. report@mail.juicey.dev)
- TRUST_PROXY_HEADERS - security feature if you trust the proxy headers giving you right ip for the job. Required if you ever want to host it
- TRUSTED_PROXY_CIDRS - linked with TRUST_PROXY_HEADERS, trusted domains / ip's in a list.
- SENTRY_DSN - sentry link for errors.
- IP_HASH_SECRET - REQUIRED. Hash secret to avoid hash lookups and get ur ip leaked
- JUICEBOX_PROD_HOST - the juicebox domain (e.g. box.juicey.dev) only required if you put it in a website
- MAX_FILE_SIZE - per-upload limit (e.g. 750MB, 1GB, or raw bytes)
- JUICEBOX_REDIS_URL / REDIS_URL - Redis (or Dragonfly) connection string used for metadata
- JUICEBOX_REDIS_PREFIX - key namespace prefix (default:
juicebox) - JUICEBOX_STORAGE_ROOT - base directory; other storage paths resolve under it
- JUICEBOX_DATA_DIR - metadata dir (default: data/)
- JUICEBOX_UPLOAD_DIR - files dir (default: files/)
- JUICEBOX_CHUNK_DIR - chunk dir (default: data/chunks)
- JUICEBOX_PUBLIC_DIR - serve static assets from a different directory
- JUICEBOX_PROD_HOST - canonical host for generated links when APP_ENV=production
- APP_ENV - set to production for prod-only checks
Juicebox stores all mutable metadata (owners, reports, IP bans, admin sessions) in Redis.
- Point
JUICEBOX_REDIS_URL(orREDIS_URL) at your Redis/Dragonfly instance. - Set
JUICEBOX_REDIS_PREFIXif you want to isolate keys per deployment (defaults tojuicebox). - The admin key still lives on disk (
admin_key.json) so you can rotate it manually if needed.
On startup the server will migrate any legacy JSON files into Redis the first time it sees empty keys. Once migrated, the JSON files are no longer written to, and Redis is treated as the source of truth. This lets you roll back easily (JSON files stay on disk) while giving you the durability and concurrency benefits of a real key-value store.
Build once:
npm install
npm run buildDev mode:
npm run build:watchTests:
npm testUse cargo-flamegraph locally to profile the Rust
server. The web flamegraph endpoint has been removed, so profiling now runs entirely from your
terminal:
cargo install flamegraph # once, installs cargo-flamegraph
sudo cargo flamegraph --bin juiceboxsudois required on Linux becauseperfneeds elevated privileges; useperf_event_paranoidtweaks if you prefer a passwordless setup.- Pass the same flags you would give
cargo runafter the--. Example:sudo cargo flamegraph --bin juicebox -- --config configs/dev.toml - The SVG flamegraph is written to
flamegraph.svgin the project root.
For lightweight sampling without perf, hit GET /debug/profile/raw while the server handles
traffic. The endpoint returns a pprof protobuf that you can open with go tool pprof or
pprof-rs.
secondsclamps between 1–60 (default 15)frequencycan be tuned via?frequency=500(10–2000 Hz)- Only
format=protobuf|pprof|pbis supported now. - The endpoint returns HTTP 429 while another capture is running to avoid overlapping samples.
Run the profiling build to emit bundle metadata and summaries under public/dist/profile/:
npm run profile:frontendArtifacts:
meta.json– full esbuild metafile for interactive analysissummary.json– machine-readable size summary (bundles + top modules)summary.md– human-friendly report with tables and quick links
Open summary.md for a quick overview, or launch the interactive analyzer:
npx esbuild-analyze public/dist/profile/meta.jsonThe profiling build uses production optimisations so you can diff bundle sizes before/after
changes. Outputs are regenerated on each run alongside the normal public/dist artifacts.
Sentry can capture errors and traces if enabled:
- SENTRY_DSN - your DSN; leave unset in dev to disable (or set to disabled/off)
- SENTRY_ENV - environment label (defaults from APP_ENV)
- SENTRY_RELEASE - release identifier; falls back to crate version/commit
- SENTRY_TRACES_SAMPLE_RATE - 0.0–1.0 (defaults to 1.0)
- SENTRY_PROFILES_SAMPLE_RATE - 0.0–1.0 (defaults to the trace rate when unset)
- Visit http://localhost:8080
- Upload a file in the web UI
- Share the direct link
API (curl):
curl -F 'file=@path/to/yourfile.png' http://localhost:8080/api/uploadJuicebox sends cache-friendly headers on file downloads. Optional automatic purge on delete when both are set:
- CLOUDFLARE_ZONE_ID
- CLOUDFLARE_API_TOKEN (with Zone.Cache Purge)
- Fork, branch, commit, push, PR
git checkout -b feature/your-feature
git commit -am 'Add new feature'
git push origin feature/your-featureMIT
- Repo: https://github.com/create-juicey-app/juicebox
- Issues: https://github.com/create-juicey-app/juicebox/issues
If you front Juicebox with a CDN such as Cloudflare the server will send cache headers on file downloads so the CDN can serve files from the edge instead of the origin. That reduces download latency and origin bandwidth use dramatically for files that are requested more than once.
Behaviour implemented by the server:
- For files with a long TTL the server will send:
Cache-Control: public, max-age=31536000, immutableso browsers and CDNs cache aggressively. - For files with a shorter remaining TTL the server will use
Cache-Control: public, max-age=<remaining_seconds>and set anExpiresheader derived from the file metadata.
When you delete a file the server will attempt to purge the corresponding edge cache entry via the Cloudflare Purge API. Purges run in the background (do not delay the HTTP delete response). Purge calls are optional and only attempted when the following environment variables are set:
CLOUDFLARE_ZONE_ID- the numeric or hex zone identifier for your siteCLOUDFLARE_API_TOKEN- an API token with theZone.Cache Purgescope for that zone
If those variables are not present the server will safely no-op and continue functioning normally (useful for local development / tests).
You can upload files via a simple POST request:
POST /api/upload
Content-Type: multipart/form-data
file=<your file>Example (using curl):
curl -F 'file=@path/to/yourfile.png' http://localhost:8080/api/uploadPull requests and issues are welcome! Please open an issue first to discuss major changes.
- Fork the repository
- Create your feature branch (
git checkout -b feature/your-feature) - Commit your changes (
git commit -am 'Add new feature') - Push to the branch (
git push origin feature/your-feature) - Open a pull request
MIT License
- Catbox for inspiration
- Rust, JavaScript, HTML, CSS communities