← Back to projects

Full-stack · 2026

Burnedfile

Self-destructing file sharing over the sender's own Google Drive. The file lands in their Drive and stays private there. Burnedfile mints a restricted link; when the link expires or its views run out, the file is hard-deleted from the sender's Drive.

Vue 3 TypeScript Fastify MySQL Google Drive API Argon2 Docker

What it honestly does

The product copy says this out loud, and so does this page.

Burnedfile controls distribution of the link, not the content. Once bytes reach a viewer, no restriction applies — screenshots, Save-As and re-uploads are unstoppable. What it actually promises is revocation of future access, plus an audit trail.

Country and IP restrictions are deterrent controls, not security boundaries. They stop casual and accidental access. They do not stop a VPN.

Files are removed within minutes of the final view or expiry, and always within 90 minutes. Not "instantly" — the architecture cannot keep that claim, so it does not make it.

Why this section exists. Most tools in this category overpromise. Writing the limits down first made the engineering decisions easier: every control below is built to be honest about what it can and cannot enforce.

How a view is decided

Every request for a shared file runs an ordered rule pipeline. The order is deliberate: a link that is dead is never denied for who the viewer is, which keeps deny reasons honest in the audit log.

Ordered policy pipeline from request to allow or deny viewer GET /l/:id policy/ — pure decision core: no I/O, no clock, no network policy_schema kill_switch link_state time_window counters byte_cap content owner_conn network_trust ip / country os password amber = deterrent control, not a security boundary ALLOW DENY + reason

api/src/policy/ has no dependencies on purpose. Time, geography, password verification and every counter are passed in, which makes the whole restriction surface testable as a table of plain values.

The two state machines

A link's lifecycle and the stored file's lifecycle are tracked separately, because a link can die long before the bytes are gone — and the gap between those two moments is exactly what the sweeper closes.

Link state machine above, content state machine below LinkState DRAFT ACTIVE SEALED CLOSED ClosedReason EXPIRED · REVOKED VIEWS_EXHAUSTED BYTES_EXHAUSTED CONTENT_VANISHED ABANDONED · ABUSE ContentState NOT_READY SERVABLE BURNING BURNED GONE

BURNING → BURNED is the window the 90-minute guarantee covers.

The interface

Screenshots go here.

My links The owner's dashboard: audit trail, expiry, revoke. assets/burnedfile-links.webp
Link settings Views, expiry, password, country and IP rules. assets/burnedfile-settings.webp
Viewer's page What the recipient sees, including a denied state. assets/burnedfile-viewer.webp
Drive connect The OAuth handoff for the sender's own Drive. assets/burnedfile-drive.webp

Layout

api/src/policy/The pure decision core — no I/O, no clock, no network. The piece that must be provably correct, so it is the piece with no dependencies.
api/src/security/Trust boundary, Argon2 password hashing, id generation, CIDR parsing.
api/src/links/Grants, view accounting, the link state machine, the egress ledger.
api/src/storage/StorageProvider interface plus the Google Drive implementation.
api/src/tasks/The sweeper and the burn.
api/src/web/Fastify routes.
api/db/migrations/Forward-only SQL.
web/Vue 3 + Vite frontend.
infra/Docker Compose, Dockerfile, nginx ALB stand-in.
No build step on the API. Node 24 strips TypeScript types at runtime, so what runs in production is the source you read.
Burnedfile — built by MilanDroid All projects