DEV Community

Ali Raza
Ali Raza

Posted on

I built a file transfer tool that can’t spy on you even if it wanted to

Uses URL fragments to keep keys off servers

I got tired of explaining privacy policies to people.

Every time I needed to send a file to someone, I had to pick a service and implicitly trust it. Trust that it wasn’t reading my files. Trust that it wasn’t training a model on my documents. Trust that when it said “we don’t look at your stuff” it actually meant it.

I couldn’t verify any of that. Neither could you.
So I built phntm.sh. And I want to be honest about what it is, what it isn’t, and where it’s still rough.

The core idea

Zero-knowledge means the server genuinely cannot read your files. Not “won’t.” Cannot.

Here’s how it works. When you drop a file into phntm, your browser generates a 256-bit AES key. The file gets encrypted client-side with AES-256-GCM before a single byte leaves your machine. Only the ciphertext goes to the server. The decryption key gets embedded in the URL fragment, the part after the #.

Here’s the important bit. Browsers never include the fragment in HTTP requests. It’s in the spec. RFC 3986. When you share a phntm link, the recipient’s browser downloads the ciphertext and decrypts it locally using the key from the fragment. My server never sees the key. Ever.

I store noise. Without the key, the ciphertext is mathematically useless.

Why open source

Because “trust us” is not an architecture.

I open-sourced both the web app and the CLI so you can verify the claims yourself. Don’t take my word for it. Read the crypto layer. Check that the key never gets sent anywhere. That’s the only honest thing to do when you’re making security claims.

The rough edges, and I mean it

I’m being honest here because this is build in public, not a product launch.

The encryption buffers the whole file in memory. For large files that’s a problem. I know. It’s on the list.

The CLI flag parsing is basic. I rolled it myself instead of using a library, which was a good learning exercise but means it’s not as robust as it should be.

I had Vercel Analytics on the page. A commenter flagged it and they were right to. The RFC holds. browsers don't send fragments in HTTP requests. But Vercel Analytics reads location.href client-side, which includes the hash, and POSTs it to their endpoint. That's a problem when the hash is your decryption key.

Fixed with a beforeSend hook that strips the fragment before the event fires. Lesson learned: audit every third party script when you're making security claims, including the ones you added without thinking.

What it’s good for right now

Sending a file to someone who doesn’t have any tools installed. They get a link, they click it, it decrypts in their browser, it downloads. No account. No app. No signup.

The file self-destructs when the timer expires. Nothing to breach after that.

What I learned building this

I built the CLI in Go as my first real Go project. Not a tutorial project. Something I’d actually use.

The thing that made Go’s I/O model click for me was wrapping io.Reader to build a progress bar. The HTTP client does io.Copy and the bar updates itself as bytes flow through. Small thing but it changed how I think about composability.

The whole CLI is stdlib-only. No external deps. That was a deliberate choice and also a good constraint for learning.

Where it lives
https://phntm.sh
https://github.com/aliirz/phntm.sh
https://github.com/aliirz/phntm-cli

Feedback welcome. Especially on the crypto layer.

Top comments (16)

Collapse
 
diamondfdk profile image
diamond sharma

Preety neat. Would check it with a friend

Collapse
 
aliirz profile image
Ali Raza

Thank you 🙏

Collapse
 
trinhcuong-ast profile image
Kai Alder

The URL fragment trick for the key is clever. RFC 3986 guaranteeing fragments never hit the server is one of those spec details that most devs don't know about but it's incredibly useful for exactly this kind of thing.

One question though - what happens if someone shares the link through a platform that strips or modifies the fragment? I've seen some chat apps and email clients do weird things with URL fragments. Do you have any fallback or at least a clear error message for that case?

Also curious about the file size limits. AES-256-GCM in the browser can get pretty memory-hungry with large files since you're buffering the whole thing. Have you looked into streaming encryption with Web Streams API?

Collapse
 
aliirz profile image
Ali Raza

Good questions. Fragment stripping is a real edge case like some Slack previews and email clients do mangle URLs. Right now there's no graceful fallback, just a broken decrypt. It's on the list. Worth adding a clear error state that says "key missing from URL, ask the sender to resend the link."

On streaming encryption yes, Web Streams API with TransformStream is the right fix for the memory buffering issue. I buffered the whole file because it was simpler for a first version. Not sustainable for large files. I will tackle it.

Collapse
 
mfc_keibisoft profile image
Marius-Florin Cristian

Pretty neat! I am also building in this space but went the "serverless" aproach! Pretty wild what you can do with the go i/o readers and writers. you can actually start tcp connections, and encrypt them by wrapping them in custom Reader/Writers.

I think for the approach you took the veteran is croc but no UI, and not browser based.

Collapse
 
aliirz profile image
Ali Raza

That TCP wrapping approach is wild, would love to see what you’re building. The composability of readers and writers in Go was honestly the thing that made the language click for me.

And yeah croc is the obvious comparison, great tool. The browser-based approach was intentional though — the recipient needs zero setup. Just a link. That’s the whole point.

Collapse
 
ashlynposter profile image
Ashlyn Poster

Hi, Ali, How are you?

I am a recruiter at techstacks-creator solution.
Currently we are looking for a contract developer for an existing project development.
Looking forward to discuss about that position with you.

Best, Ashlyn

Collapse
 
aliirz profile image
Ali Raza

Hi Ashlyn, happy to hear more if you can share the role details and budget upfront.

Collapse
 
alen_p profile image
Alen P.

This is actually pretty cool. The "can't spy even if it wanted to" part is what got me, feels like more tools should be built with that mindset.

Collapse
 
skhmt profile image
Mike 🐈‍⬛ • Edited

It actually could spy on you if it wanted to, it would just have to POST the url fragment.

It doesn't seem to do it without a small code change though.

Collapse
 
aliirz profile image
Ali Raza

Fair point and worth saying clearly. The zero-knowledge claim holds as long as the JavaScript running on the page is what it says it is. If I pushed malicious code that POSTed the fragment, the architecture breaks. That's why open source matters here as you can audit what's actually running. It's trust in the code, not trust in me.

Collapse
 
fredbrooker_74 profile image
Fred Brooker

JUST a UI note: it's nearly unreadable on a mobile phone

Collapse
 
botanica_andina profile image
Botánica Andina

This is a really practical take. The memory/context management piece is something I've been wrestling with too — especially when building autonomous tools that need to maintain state across sessions.

Collapse
 
aliirz profile image
Ali Raza

Thanks, though I think you might be thinking of a different post. This one is specifically about client-side encryption for file transfer.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.