Skip to content
bairesPublic

About

Instant File Sharing via Cloudflare R2

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

yz — Instant File Sharing via Cloudflare R2

yz is a single-binary CLI for instant file sharing backed by Cloudflare R2: type yz myfile.png, get a ready-to-paste public URL. Recipients just click the link; no authentication needed on their side.

  • Single static binary: no AWS SDK, no runtime dependencies; under 10 MB for macOS and Linux (amd64 and arm64).
  • Fast: terminal-to-link in under ~5 seconds.
  • Unguessable keys: files are uploaded under <16-char random base62>/<filename> prefixes.
  • Custom domains & r2.dev: automatic detection and configuration of managed r2.dev or custom domains.
  • Time-bounded & signed shares: --expires and --signed for presigned URLs.
  • Local history: yz list shows previous shares, newest first.
$ yz screenshot.png
https://pub-yourbucket.r2.dev/3kK9Xw0Lp2QmZb8N/screenshot.png

Installation

Homebrew (macOS)

brew install baires/tap/yz

Publishing a stable release (vX.Y.Z) updates the Homebrew tap immediately, so Formula/yz.rb does not wait for the tap's hourly poll. Prereleases are skipped.

Install script (macOS & Linux)

curl -fsSL https://raw.githubusercontent.com/baires/yz/master/install.sh | sh

The script downloads the latest release for your OS and architecture, verifies the published SHA-256, and installs yz onto your PATH. Pin a version or install directory with --version / --bin-dir; see install.sh --help.

Go

go install github.com/baires/yz/cmd/yz@latest

Manual download

Download a static binary from Releases — names are yz_<tag>_<os>_<arch> — then:

chmod +x yz_*
mv yz_* /usr/local/bin/yz

Updates

Release builds look for a newer version at most once every 24 hours. Startup only reads a cache file, ~/.config/yz/update.json (mode 0600). The GitHub request runs in the background while the command runs, with a short timeout, and never runs for a development build, when stdout or stderr is not a terminal, when CI is set, or when YZ_NO_UPDATE_CHECK=1.

When a newer release is already known, yz prints one line to stderr after the command:

yz 1.4.0 is available, run yz update
yz update           # download the asset for this OS, verify SHA-256, replace this binary
yz update --check   # report only

Each GitHub release includes checksums.txt next to assets named yz_<tag>_<os>_<arch>. yz update checks the downloaded file against that manifest before it replaces the binary. A Homebrew install prints brew upgrade yz instead of overwriting itself. A go install copy prints go install github.com/baires/yz/cmd/yz@latest.

Background installs are off by default. Turn them on with yz config auto-update on or YZ_AUTO_UPDATE=1. The next time a newer release is available, yz downloads it, verifies the checksum, and swaps the binary in after the command so the following run uses it, then prints yz updated to 1.4.0. Package-manager installs are never replaced.

Quickstart

Before you start: enable R2

R2 has to be activated once on the Cloudflare account you want to use. The free tier is enough, but Cloudflare asks you to accept the terms and add a payment method (a card) in the dashboard before the API will list buckets. Until then, bucket calls fail with HTTP 403, error code 10042, and the message Please enable R2 through the Cloudflare Dashboard.

Open the R2 overview for that account. yz setup prints this link after you pick one:

https://dash.cloudflare.com/<account_id>/r2/overview

yz setup lists buckets before it asks you to choose one. When it sees error 10042, it stops on an Enable R2 step, explains the one-time activation, and offers to open the dashboard ($BROWSER when that is set, otherwise the system browser). It checks again every few seconds and continues at the bucket step as soon as R2 is enabled. You do not need to quit and run setup again. Press ctrl+c to cancel.

The account step also reminds you that R2 must be enabled before you pick a bucket.

Troubleshooting a 403 during setup

Cloudflare uses the same HTTP 403 for three different problems. yz setup reads the error code in the JSON body (R2 error codes) and tells them apart:

  • R2 is not enabled — 10042 NotEntitled, message Please enable R2 through the Cloudflare Dashboard. Activate R2 at the link above. Setup waits and continues on its own.
  • The login is missing the R2 scope — 10003 AccessDenied, or 10000 Authentication error when the token lacks permission for the call. Run yz setup and log in again so the token includes workers-r2.read.
  • The account does not match the login — 9109 (unauthorized to access that account) or 7003 (the account id did not route). Run yz setup and choose an account this login can access.

Any other HTTP 403 still asks you to check account access and R2 permissions, then run yz setup again.

1. One-time setup

yz setup

Setup walks you through: browser OAuth login to Cloudflare → picking your account (with a reminder to enable R2) and R2 bucket → choosing a public URL base (existing custom domain, managed r2.dev, or private) → pasting an R2 API token with Object Read & Write. If R2 is not enabled yet, setup waits on the dashboard step instead of failing. Credentials are validated and saved to ~/.config/yz/config.json with 0600 permissions.

Interactive setup demo

Watch setup as MP4.

2. Share files

$ yz presentation.pdf
https://pub-yourbucket.r2.dev/8xV1K2m9Qp4Lw0Zb/presentation.pdf

Only the URL goes to stdout; progress, clipboard notices, and errors go to stderr. On an interactive terminal the link is copied to your clipboard automatically, and the completion screen lets you press o to open it in a browser.

File upload demo

Watch upload as MP4.

Usage & Options

usage: yz [--signed] [--expires 24h] [--domain HOST] [--clipboard] [file | -]
Flag Description
(default) Public URL via your bucket's custom domain or r2.dev
--expires 2h Presigned URL that R2 rejects after the given duration (1s–7d)
--signed Force a presigned URL even when a public base is configured
(no file) Upload the image on the clipboard (macOS, or Linux with wl-paste or xclip)
- Read the file from stdin instead of a path
--clipboard Upload a copied file, image, or text (macOS)
--domain HOST Override the configured URL base for this share
yz site <dir|zip> Publish a static site at <domain>/s/<id>/
yz list Show previous shares, newest first
yz version Print the version
yz update Install the latest release after verifying its SHA-256
yz update --check Report whether a newer release exists
yz config auto-update on Download newer releases in the background
yz --expires=2h secret_report.pdf      # expires automatically
yz --signed internal-doc.docx          # presigned even with a public domain
yz --domain=cdn.example.com banner.webp
tar -c ./notes | yz -                  # upload stdin, no temp file of yours
yz                                     # image currently on the clipboard
yz --clipboard                         # macOS: copied file, image, or text
yz list                                # previous shares, newest first

yz list prints each share's time, size, filename, expiry, and URL. On a terminal it is an interactive table: enter copies the selected link, d (or Delete) asks for confirmation and then deletes the share from R2 and history (a site removes everything under sites/<id>/; the Worker stays), q quits. Piped output stays plain text. History is stored locally in ~/.config/yz/shares.json (0600). Presigned links, including the private-bucket fallback, are marked with their expiry.

CLI help demo

Watch help as MP4. See recording instructions to regenerate the demos with VHS.

Static sites

$ yz site ./dist
https://files.example.com/s/3kK9Xw0Lp2QmZb8N/

yz site uploads a folder (or a .zip; a single top-level folder is unwrapped) to R2 under sites/<id>/. It needs index.html at the site root and skips symlinks and OS junk like .DS_Store. A small Worker, yz-sites, is embedded in the binary and deployed to your account through the Cloudflare API (no wrangler or Node): it binds your bucket and routes <your custom domain>/s/*, serves index.html for directories, falls back to the root index.html for extension-less paths (SPA routing), and serves 404.html when present.

Static site demo

Watch the site demo as MP4.

yz setup deploys the Worker when the bucket has a custom domain; otherwise the first yz site does. Sites require a custom domain on a zone in the same account: r2.dev hosts cannot run Workers. The login requests workers-scripts.admin, workers-routes.write, and zone.read for this; logins saved before this release need yz setup once more to grant them.

Security

  • Hand-rolled SigV4: streaming PUT and presigned GET signing with no AWS SDK dependency.
  • Streaming uploads: files stream from disk to R2 without buffering whole payloads in memory.
  • Path confinement: os.OpenRoot prevents symlink escapes outside the target directory.
  • Strict permissions: config is saved 0600 and world-readable configs are refused.
  • No usage telemetry: sharing talks only to Cloudflare. A release check may contact GitHub at most once a day; set YZ_NO_UPDATE_CHECK=1 to turn that off.

See SECURITY.md for how to report vulnerabilities.

Environment Overrides (testing & advanced)

  • YZ_CONFIG_DIR: configuration directory (default ~/.config/yz)
  • YZ_OAUTH_BASE_URL: override Cloudflare OAuth endpoints
  • YZ_OAUTH_CLIENT_ID: override the built-in OAuth client ID (for forks that register their own Cloudflare OAuth client)
  • YZ_API_BASE_URL: override Cloudflare Client v4 API endpoint
  • YZ_R2_ENDPOINT: override the R2 S3 endpoint
  • YZ_NO_UPDATE_CHECK: set to 1 to skip the background release check
  • YZ_AUTO_UPDATE: set to 1 to install a newer release in the background
  • YZ_GITHUB_BASE: release host (default https://github.com; tests may use http://127.0.0.1)
  • YZ_REPO: GitHub owner/name that publishes releases (default baires/yz)

Contributing

Contributions are welcome — see CONTRIBUTING.md for the development setup, test workflow, and how pull requests turn into releases. Please follow the Code of Conduct.

License

Apache 2.0

About

Instant File Sharing via Cloudflare R2

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages