Skip to content

create-juicey-app/juicebox

Repository files navigation

Juicebox

Fast Catbox-like hotlink file share.

Juicebox is a lightweight, high-speed file hosting service with direct hotlinking. Upload, share, done.

Quick Start

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 32

Run:

cargo run --release

Open http://localhost:8080

Configuration (env)

Common 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

Persistence & migrations

Juicebox stores all mutable metadata (owners, reports, IP bans, admin sessions) in Redis.

  • Point JUICEBOX_REDIS_URL (or REDIS_URL) at your Redis/Dragonfly instance.
  • Set JUICEBOX_REDIS_PREFIX if you want to isolate keys per deployment (defaults to juicebox).
  • 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.

Frontend (will be deprecated)

Build once:

npm install
npm run build

Dev mode:

npm run build:watch

Tests:

npm test

Profiling

Backend (Rust)

Use 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 juicebox
  • sudo is required on Linux because perf needs elevated privileges; use perf_event_paranoid tweaks if you prefer a passwordless setup.
  • Pass the same flags you would give cargo run after the --. Example: sudo cargo flamegraph --bin juicebox -- --config configs/dev.toml
  • The SVG flamegraph is written to flamegraph.svg in 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.

  • seconds clamps between 1–60 (default 15)
  • frequency can be tuned via ?frequency=500 (10–2000 Hz)
  • Only format=protobuf|pprof|pb is supported now.
  • The endpoint returns HTTP 429 while another capture is running to avoid overlapping samples.

Frontend (bundle)

Run the profiling build to emit bundle metadata and summaries under public/dist/profile/:

npm run profile:frontend

Artifacts:

  • meta.json – full esbuild metafile for interactive analysis
  • summary.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.json

The 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.

Telemetry (optional)

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)

Usage

API (curl):

curl -F 'file=@path/to/yourfile.png' http://localhost:8080/api/upload

CDN / Cloudflare

Juicebox 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)

Contributing

  • Fork, branch, commit, push, PR
git checkout -b feature/your-feature
git commit -am 'Add new feature'
git push origin feature/your-feature

License

MIT

Links

Cloudflare / CDN notes

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, immutable so 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 an Expires header 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 site
  • CLOUDFLARE_API_TOKEN - an API token with the Zone.Cache Purge scope for that zone

If those variables are not present the server will safely no-op and continue functioning normally (useful for local development / tests).


API

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/upload

Contributing

Pull requests and issues are welcome! Please open an issue first to discuss major changes.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/your-feature)
  3. Commit your changes (git commit -am 'Add new feature')
  4. Push to the branch (git push origin feature/your-feature)
  5. Open a pull request

License

MIT License


Acknowledgements

  • Catbox for inspiration
  • Rust, JavaScript, HTML, CSS communities

Links

About

Fast Catbox like hotlink file share.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •