Here's a small annoyance that shows up ten times a week: I have a file on my machine — a screenshot, a GIF of a bug, a PDF, an exported chart — and I need to hand someone a link to it. Not the file as an attachment. A link. Something I can paste into a GitHub issue, a Slack message, a WhatsApp thread, an email.

Attachments are clumsy. GitHub issues want a URL. Slack mangles the preview. And half the time the person is on their phone and just wants to tap and see the thing.

The "proper" tools all felt heavier than the problem. Google Drive gives you a link, sure — but then it's the Drive link, with its preview page, its "request access" wall, its sharing settings I have to remember to loosen. I don't want a document. I want a raw file at a raw URL.

So I did the lazy thing: I taught Claude CodeClaude Code Anthropic's command-line tool. You build software by talking to an AI right in the terminal — it reads your code, edits files, and runs tests. to upload the file to a public bucketbucket A folder in the cloud where you store files. The name comes from the idea of a container you just drop things into. Each file gets its own web address. and give me back the URL. Now I just say "upload this and give me the link" and I get back a plain https://… I can paste anywhere.

The whole "skill" is three lines of config

There's no clever code here. The entire thing lives in my CLAUDE.mdCLAUDE.md A plain-text file where you write instructions for Claude Code to follow in a project. Think of it as a note you leave for the AI: "here's how we do things around here." — the file where I leave standing instructions for the AI. It boils down to:

## Quick shareable links
- Public bucket: gs://my-public-assets/
- Public URL: https://storage.googleapis.com/my-public-assets/<filename>
- Upload: gsutil cp <file> gs://my-public-assets/uploads/

That's it. When I need a link, Claude Code reads those lines, runs the upload, and constructs the URL:

gsutil cp ~/Desktop/bug-repro.gif gs://my-public-assets/uploads/bug-repro.gif
# → https://storage.googleapis.com/my-public-assets/uploads/bug-repro.gif

One command in, one link out. The file is live on Google Cloud StorageGoogle Cloud Storage Google's service for storing files in the cloud. Same idea as a hard drive, but on Google's servers — and each file can have its own public web address., served from Google's CDNCDN A network of servers spread around the world that keeps copies of your files close to whoever's downloading them. That's why the link loads fast for anyone, anywhere., fast from anywhere in the world. I paste it into the issue and move on.

Why Google Cloud, of all things

Honestly? Not because it's the best. Because I'm already logged in.

I use gcloudgcloud Google Cloud's command-line tool. You log in once, and after that everything you do on Google's cloud goes through it — no password prompts. for other things, so the credentialscredentials The digital keys that prove who you are to a service, like a saved login. Once set, your tools use them on their own — no password prompt each time. are sitting there in my terminalTerminal A window where you type text commands to control the computer, instead of clicking buttons and menus. Developers use it all the time., authenticated, ready. There's no new account to make, no key to generate, no token to paste. The gsutilgsutil A command-line tool from Google for moving files in and out of the cloud. One line copies a file up and it's online. upload just works. The whole appeal of this trick is that it's frictionless — and the friction I was avoiding was setting up the thing that avoids friction.

This is the part worth stealing, not the specific provider. Use whatever cloud you're already inside of. If your day job lives on AWS, S3S3 Amazon's file-storage service, the most popular in the world. It works like a hard drive in the cloud, where each file can get its own web address. does the exact same job — a public bucket, one aws s3 cp, a public URL. If you're deep in Google Drive, even Drive can hand out direct file links if you set the sharing right. The winning move isn't picking the "correct" storage. It's picking the one where you're already authenticated, so the cost of getting a link drops to a single command.

The one rule: nothing that matters

A public bucketpublic bucket A cloud folder set so anyone with the link can open its files — no login. Great for sharing, terrible for anything you'd mind a stranger seeing. means exactly what it says. Anyone with the URL opens the file — no login, no check, nothing. And URLs leak: they end up in someone's browser history, a forwarded message, a search index someday. So the rule I never break:

Only non-sensitive assets go here.

Screenshots of open source projects. GIFs of bugs. Illustrations for a blog post. Public documentation. Diagrams I'd happily tweet. If a file would be fine on a billboard, it's fine in the bucket. Anything with a password, a client's private data, an internal number — that never touches this. For those, I encrypt before publishing (I wrote about that trick separately), or I use a tool built for access control.

Keeping the two worlds strictly separate is what makes the lazy version safe. The bucket isn't "insecure" — it's deliberately public, and I only ever feed it things that are meant to be public anyway.

The one config I did have to change: kill the listing

Here's a gap I walked straight into, and it's worth flagging because the easy path leads you right past it.

The command everyone reaches for to make a bucket public is this:

gsutil iam ch allUsers:objectViewer gs://my-public-assets

Looks innocent. It hands the world an IAMIAM The cloud's permission system — who can do what. It's the guest list: it decides whether someone can read a file, list a folder, or nothing at all. role called objectViewer. The problem is what that role actually bundles: it's not just "read a file if you have its link" (storage.objects.get) — it also includes "list everything in the bucket" (storage.objects.list). Those are two very different powers, and the second one is the leak.

Because with listinglisting Asking a folder for its full table of contents — every file name inside it. Reading one file is different from being handed the whole index. on, anyone — no login, no credentials — can hit the bucket's index endpointendpoint A specific web address a program calls to ask a service for something. Here, the one that hands back the bucket's full file list. and get back the full inventory of every file I've ever dropped in there. Even files I assumed were "hidden" behind an obscure name. The name stops being a secret the moment someone can just ask for the whole list. "Non-sensitive" was my rule for each file, sure — but I still didn't want a stranger paging through the complete catalog of my uploads like a directory.

The fix is a one-role swap. Drop the over-broad role and grant the narrow one instead:

# remove the role that also allows listing
gsutil iam ch -d allUsers:objectViewer gs://my-public-assets

# grant read-a-file-by-name only — no listing
gsutil iam ch allUsers:legacyObjectReader gs://my-public-assets

legacyObjectReaderlegacyObjectReader A Google Cloud permission that lets someone download a file if they know its address, but not browse the folder. The narrow, safer kind of "public". grants only storage.objects.get. Direct links keep working exactly as before; the bucket's table of contents goes dark. This is the whole difference between "my files are public" and "my folder is public" — I want the first, never the second.

The proof it's closed is one anonymous request:

curl -s "https://storage.googleapis.com/my-public-assets?list-type=2"
# → AccessDenied: ...does not have storage.objects.list access...

If that comes back AccessDenied, listing is off. If it comes back with a tidy XML list of your files, you're still exposed — go do the swap. Same idea on S3, by the way: s3:GetObject for the public and never s3:ListBucket. The listing permission is the one that quietly turns "a few shared files" into "a browsable archive of everything."

Why this beats a "real" upload tool

Every dedicated file-sharing tool wants to be a product. It has an account, a dashboard, a free tier with a catch, an expiring-links feature I have to configure, a UI I have to open. That's a lot of ceremony for "give me a URL for this GIF."

The bucket has none of it. It's a folder. Files go in, links come out, they stay up forever (or until I clean them out), and there's nothing to log into. The instruction lives in my CLAUDE.md, so the AI does the actual typing — I don't even remember the bucket path. I just describe what I want.

That's the whole thing. No app, no service, no signup — just a public folder in a cloud I was already standing in, and three lines telling the AI how to use it.

The best tool is often the one you already have open.