Private R2 drop workspace for controlled file release, short-lived capability sharing, and durable operator ownership.
Cloudflare Workers · R2 · D1 · Server-rendered HTML/CSS/JS · GPL-3.0
- What It Does
- Stack
- Changelog
- Architecture
- Project Structure
- Quick Start
- Documentation
- Security Model
- Legal Position
- Notes
- License
BurnBox is a private, operator-owned workspace for file storage and controlled distribution. It is deliberately not a public upload site.
- files live in R2 storage you own and control
- external access is a revocable capability link, not a permanent URL
- administration happens in a private workspace behind owner-account authentication
- expiration, download limits, and revocation are first-class controls
The public link does not expose your admin surface. The admin surface does not expose your public links. They run on separate domains and evolve independently.
The screenshot shows an experimental deployment. Colors, labels, layout, and file controls are yours to change — fork the repo and make it your own.
- Cloudflare Workers — routing, session enforcement, upload coordination, share validation, response delivery
- Cloudflare R2 — durable object storage with native Workers multipart binding
- Cloudflare D1 — file metadata, upload state, share state, audit records
- Server-rendered HTML, CSS, and JavaScript — no build step, no frontend framework, minimal deployment surface
- adds
docs/en/maintenance.md— SQL scripts and Cron Trigger guidance for periodic cleanup ofauth_events,audit_logs, and expired token records; includes retention windows and an automated cleanup scaffold - adds
docs/en/privacy-policy-template.md— a deployer-ready privacy policy template covering GDPR, CCPA/CPRA, and China's PIPL; documents all data categories collected by a BurnBox deployment, retention periods, cookie declaration, and data-subject rights - updates
docs/en/legal-risk-statement.mdto reference the privacy policy template from the deployer responsibility section - adds
licensefield (GPL-3.0-only) topackage.json - bumps version to 2.3.1
Developer guidance for this release:
Older changelog entries
- adds
GET /api/files/upload-status— a server-side query endpoint that returns confirmed part numbers, plan status, and next-part pointer from durableupload_partsstate; the server is the authority on multipart truth, not the browser - client now queries upload status before starting the part loop; already-confirmed parts are skipped and progress reporting is aligned to the actual resume position
localStoragerecords thefileId, filename, file size, and chunk geometry afterinit-upload; on page refresh the workspace detects a pending record and shows a resume banner- resume is triggered naturally: selecting the same file (matched by name and size) in the upload form automatically uses the existing
fileIdand resumes from the confirmed breakpoint — no separate file picker or extra interaction - dismissing a pending resume banner calls
abort-upload, cleaning up the R2 incomplete multipart and the D1 upload plan immediately rather than waiting for R2's automatic expiry - adds Claim-page UX hardening: a required confirmation checkbox before
Enter workspacebecomes active, and a setup-key source explanation for operators who did not configureCLAIM_KEY - adds a Deployment status card visible to logged-in owners: shows private entry path, workspace host, share host,
SHARE_LINK_SECRETconfiguration state with a warning when absent, recovery email, and hostname-style sharing toggle - adds a dismissible first-deploy guidance banner when
APP_ENTRY_PATHis not configured, surfacing the configuration option for operators who did not know it existed
Developer guidance for this release:
- separates the monolithic workspace inline script into five focused client modules:
helpers,share,files,upload, andboot-wiring layout.jsnow composes the page script from imported modules instead of carrying all frontend JS detail inline- preserves
boot.apiBaseandboot.appEntryPathas the sole source of private API paths — no bare/api/...strings reintroduced - keeps Logout, Refresh, Upload, share create/revoke, and all account security actions stable under prefixed private-entry routes
- no product behavior changes, no new API routes, no new capabilities — structural maintainability pass only
- establishes a cleaner frontend module boundary ahead of the resumable upload work in 2.3.0
- ships deployment-managed private workspace entry support through
APP_ENTRY_PATH - moves private workspace pages and private API routes under the derived private entry prefix instead of exposing the admin surface at the root path by default
- adds operator-visible
Private entrydisplay inside the workspace without making the route editable from the UI - adds upload-diagnostics aggregation for unfinished or failed uploads so operators can inspect multipart progress from durable server-side state
- hardens multipart consistency with explicit abort cleanup on failed uploads and compensating object deletion when metadata commit fails after multipart completion
- adds a private-entry smoke check to keep prefixed workspace routing from regressing
- keeps the owner-account auth baseline from 2.2.0 intact while preparing a smaller frontend-JS refactor before resumable upload
Developer guidance for this release:
- Quickstart
- Deployment
- Architecture
- Development Plan
- Release Checklist
- Troubleshooting
- Documentation index
- ships owner-account authentication inside the product instead of relying on a long-lived deployment password
- adds
Claim your BurnBoxfor first-run setup andUpgrade your BurnBox securityfor legacyADMIN_PASSWORDdeployments - moves password change, recovery-email management, backup-code regeneration, logout, and device-session control into the workspace
- hardens auth behavior with generic invalid-credential logging, recovery lockouts, legacy-login throttling, claim-token atomicity, and password-hash sanitization
- keeps public share delivery, multipart upload, and stable
/h/{publicHandle}links intact while upgrading the workspace auth model - refreshes the public README and operator docs so deployment, migration, upgrade, and recovery behavior all describe the shipped 2.2.0 system
- adds a legal-risk documentation baseline that clarifies BurnBox as a self-hosted tool author project and assigns deployment compliance duties to instance operators
Developer guidance for this release:
- moved multipart assembly fully onto native Workers R2 APIs and removed the extra S3-compatible signing hop
- clarified retry ownership so transient recovery lives in the client instead of expanding Worker execution paths
- tightened multipart completion behavior and reduced avoidable per-part coordination overhead
- validated stable multipart transfers from
419parts through4.3 GB / 870 partsand11 GB / 2200 parts, reinforcing the cumulative-reliability diagnosis - rewrote the public docs to explain why large-file edge upload is a stateful systems problem rather than a simple timeout problem
- established three graduate-level research directions for the project: resumable multipart protocols, cost-aware coordination state, and capability-oriented public distribution
- established resumable upload as the next engineering baseline to reduce restart cost after interruption
- refined public-facing failure interaction so external entry points now present tighter and more consistent error behavior under invalid or unavailable requests
Developer guidance for this release:
- Concurrent Chunked Upload Design
- Architecture
- Share Link Delivery Architecture
- Development Plan
- Documentation index
- shipped split-domain sharing with a private workspace domain and a public share domain
- introduced
public_handleas the stable public identifier for share links - changed the default stable share URL to
https://relay.example.net/h/{publicHandle} - kept legacy
/s/{token}links for compatibility instead of breaking existing shares - removed the mandatory share landing page from the default flow and restored direct-download behavior
- fixed cross-device
Copy linkbehavior by making active share URLs reconstructable on the server - documented Cloudflare DNS, route, and certificate constraints for hostname-style sharing
- rebuilt BurnBox around a single Cloudflare Worker, R2, and D1 architecture
- replaced the legacy public-upload flow with a private admin workspace
- introduced signed admin sessions and hashed share-token storage
- redesigned the interface, share controls, and documentation structure for public release
- moved the upload path from optimistic single-request transfer to a chunked multipart model
- adopted 5 MiB chunk slicing for stability-first transfer behavior
- added D1-backed upload plans and uploaded-part tracking
BurnBox 2.3.0 is organized around six layers:
1. Split-domain delivery
A private workspace domain (console.example.com) handles authenticated operations. A public share domain (relay.example.net) handles external file delivery. The two domains share one Worker but serve different route surfaces.
2. Private-entry routing
The workspace and all authenticated API routes can be moved behind a deployment-managed prefix such as /ops via APP_ENTRY_PATH. The prefix changes where the admin surface lives without changing product behavior.
3. Owner-account authentication New deployments complete a one-time claim flow inside the product. Legacy deployments upgrade from a deployment password to a full owner account. Password rotation, backup codes, optional recovery email, and session reset all live inside the workspace — not in environment variables.
4. Chunked multipart upload Files are sliced into 5 MiB parts and assembled via R2 native multipart APIs. Upload state is persisted in D1 so the server can reason about part truth independently of the browser. Client-side retry handles transient failures with exponential backoff; the Worker execution path stays short.
5. Frontend module separation
The workspace client script is organized into five focused modules: helpers, share, files, upload, and boot-wiring. All API paths are derived from boot.apiBase — no bare /api/... strings exist in client code.
6. Resumable upload
The server holds confirmed part truth in upload_parts. On upload start or re-entry, the client queries GET /api/files/upload-status to retrieve already-confirmed parts and skips them, resuming from the first missing position. localStorage records the upload plan identifier after initialization. On page refresh, selecting the same file by name and size automatically resumes the interrupted transfer — no separate interaction required.
The current share model uses stable public_handle identifiers reconstructable from D1 state, which fixes the cross-device Copy link problem that token-hash-only storage could not solve.
Design notes on engineering difficulty
BurnBox looks small at the repository level. Its hardest problem is not UI or routing — it is edge-native large-file transfer under real network volatility.
One upload expands into a long-lived distributed pipeline: browser slicing, repeated Worker requests across hundreds of parts, R2 multipart assembly, D1 state bookkeeping, and a final readiness transition. For small files many design mistakes stay invisible. For larger artifacts they become operator-visible faults.
The practical lesson: large-file upload on the edge is not one request that happens to be bigger. It is a multi-stage reliability problem whose failure probability accumulates with every additional part. BurnBox has validated stable completion through 4.3 GB / 870 parts and 11 GB / 2200 parts.
Common misconceptions that produce the wrong fixes:
- large uploads do not usually fail because of one mysterious size threshold
- network instability is not a frontend-only problem
- transfer completion is not the same as file readiness
- adding retries is not the same as building a recoverable upload protocol
- edge infrastructure does not remove the need for explicit intermediate state
Read more: Architecture · Concurrent Chunked Upload Design
Research directions
BurnBox is also a compact research vehicle for edge-native file control systems.
How should a Worker-mediated upload protocol represent partial truth so that a large transfer can resume from failure without trusting the browser as the source of record and without forcing a whole-upload restart?
What is the minimum persistent coordination state required to preserve correctness for multipart upload on the edge when part counts become large and per-part state writes begin to dominate reliability or cost?
How can edge-native file systems separate durability, public reach, and revocation into independently controllable layers without collapsing back into either fully public storage or opaque backend mediation?
The project's thesis is that edge reliability is usually not blocked by missing infrastructure primitives. It is blocked by weak state models, misleading progress semantics, and under-specified recovery behavior.
src/
worker.js Worker entrypoint and route handling
lib/
http.js response helpers, cookie parsing, timing-safe comparison
audit.js audit log write helper
auth.js owner auth, password hashing, claim, upgrade, recovery
session.js signed session handling
files.js upload planning, part upload, multipart completion, deletion
shares.js share creation, revoke, view/download resolution
repository.js file list query layer and active share projection
layout.js workspace page rendering, composes client modules
auth-layout.js claim, sign-in, upgrade, and recovery page rendering
client/
helpers.js global state, DOM refs, apiUrl(), formatBytes, status helpers
share.js share link storage, sync, composer open/close, copy
files.js file table rendering, event delegation for all file actions
upload.js chunked upload, per-part retry with exponential backoff
boot-wiring.js init block, all event listeners, boot coordination
migrations/
0001_initial.sql initial D1 schema
0002_upload_plans.sql upload plan table
0003_multipart_uploads.sql multipart upload state extensions
0004_share_public_handle.sql stable public share handle support
0005_owner_auth.sql owner account, claim token, recovery code, auth events
If you want an AI assistant to carry out the setup with you, use the handoff file first: AI Deployment Handoff
Before you begin, decide two things:
APP_ENTRY_PATH— serve the private workspace at/or behind a prefix like/ops- Recovery path — backup codes only, or backup codes plus a recovery email
Setup steps:
npm installCopy wrangler.toml.template to wrangler.toml and fill in your Worker name, D1 database id, R2 bucket name, workspace hostname, and share hostname.
Apply the schema:
npx wrangler d1 execute <your-d1-database-name> --remote --file=./migrations/0001_initial.sql
npx wrangler d1 execute <your-d1-database-name> --remote --file=./migrations/0002_upload_plans.sql
npx wrangler d1 execute <your-d1-database-name> --remote --file=./migrations/0003_multipart_uploads.sql
npx wrangler d1 execute <your-d1-database-name> --remote --file=./migrations/0004_share_public_handle.sql
npx wrangler d1 execute <your-d1-database-name> --remote --file=./migrations/0005_owner_auth.sqlConfigure secrets:
npx wrangler secret put SESSION_SECRET
npx wrangler secret put SHARE_LINK_SECRET
npx wrangler secret put CLAIM_KEYSHARE_LINK_SECRET is required — public share downloads will fail with 503 without it. Keep SESSION_SECRET and SHARE_LINK_SECRET as separate values. CLAIM_KEY is a one-time setup key, not the long-lived owner password; omit it to use a log-generated claim code instead.
npm run deployAfter deploy: complete Claim your BurnBox (new deployment) or Upgrade your BurnBox security (legacy). Save the backup codes before closing the browser — they are shown once.
English: AI Deployment Handoff · Quickstart · Deployment · Architecture · Share Link Delivery Architecture · Concurrent Chunked Upload Design · Development Plan · Troubleshooting · Maintenance and Data Retention · Privacy Policy Template · Legal Risk Statement · Repository Boundaries
Japanese: AI デプロイ引き継ぎ · クイックスタート · デプロイ · アーキテクチャ · 共有リンク配信 · 並行チャンク分割アップロード · 開発計画 · トラブルシューティング · 法的リスク声明 · リポジトリ境界
- owner passwords are hashed with PBKDF2 and never returned to the client
- sessions are HMAC-signed with expiry; the workspace is private and session-protected
- share tokens are stored as hashes, not plaintext;
public_handleis a public identifier, not a secret - recovery-code reset is rate-limited and returns generic failure responses
- legacy upgrade login is throttled so the transitional password path is not left unbounded
- share capability can expire, be exhausted, or be revoked at any time
- download responses use
Cache-Control: private, no-store - the public share domain does not expose authenticated API routes or the workspace root
Read CONTRIBUTING.md before opening a pull request. Read SECURITY.md before reporting a vulnerability.
BurnBox is self-hosted source code, not a hosted service. The project author does not operate third-party deployments and accepts no liability for how forks or instances are used.
Before you fork or deploy BurnBox, read the full Legal Risk Statement and Repository Boundaries. These documents exist because the obligations of running a file-distribution system are real and vary significantly by jurisdiction. Do not skip them.
If you fork, deploy, or operate a BurnBox instance — especially one that serves other people's files or is accessible to the public — you are responsible for:
- conducting your own legal assessment of applicable laws in your jurisdiction before deployment
- complying with all relevant local, national, and international laws and regulations
- data protection and privacy obligations (such as GDPR, PIPL, or equivalent frameworks)
- copyright, takedown, and notice-and-action requirements
- establishing and publishing an operator contact route for abuse and legal inquiries (
abuse@your-domain.example) - any platform terms of service imposed by your infrastructure providers
BurnBox must be used strictly in accordance with the laws of your jurisdiction. Operating an instance in a legally non-compliant manner is solely the responsibility of the operator, not the upstream project.
Abuse or legal complaints about content on a third-party BurnBox deployment must go to the operator of that deployment, not to this repository. This project does not have visibility into, control over, or responsibility for the content of any third-party instance.
Every version of BurnBox has answered a different question, though the questions were rarely technical in the narrow sense.
The first was whether large files could move through the edge at all. They could — but the lesson was not about bandwidth. It was about state. A system that cannot say what it knows after a failure has not merely lost data; it has lost its account of itself. Reliability is not a timeout problem. It is a memory problem.
The second was about identity. A share link is a capability — bounded, revocable, attached to a specific act of trust. When a link's stable identity and its secret material collapse into the same thing, the system loses the ability to say clearly who still has access and why. Separating public_handle from the token hash was not a refactor. It was a clarification of what kind of thing a link is.
The third was about ownership. A deployment password in an environment variable is not an owner. It is a shared secret with no memory, no recovery path, and no face. Moving authentication into the product was an attempt to make the workspace answer to someone, not merely open for something.
The fourth was about recovery. An interrupted upload is not a failed request — it is a system in a partially-committed state. The browser's memory of the transfer does not survive a page refresh. The server's record of each confirmed part does. The answer was to make the server the authority on part truth, and to let the client ask what the server already knows before deciding what remains to be done. That is not a clever trick. It is the only design that keeps recovery honest.
What remains is the question of time — whether a system that has paused for hours or days can resume as confidently as one that paused for seconds. That is a harder problem, and one that will require the system to say something more precise about when a partial state is still worth returning to.
A small system should remain legible to a single careful reader. That is not a constraint. It is a form of respect.
Some changes look frightening only because Git is honest about time, consequence, and the cost of pretending nothing moved.
The Japanese documentation is a small gesture toward the idea that technical work should be readable in more than one language. It is also a quiet thank-you to the friends at Kyoto University who reminded me that software is never only about systems. It is also about the people who make the work feel worth carrying forward.
This project is released under the terms of the GPL v3.
Last updated: April 27, 2026 at 8:27 AM PDT
