Skip to main content

Upload files/folders to Cloudflare R2 and get a shareable link

Project description

rink

Rink uploads any file or folder from your terminal directly to a Cloudflare R2 bucket and gives you a link!

R2 objects are private by default, so rink gives you two kinds of link:

  • Presigned (default) — a signed, self-expiring URL (up to 7 days). No bucket config needed.
  • Public — a permanent https://pub-xxxx.r2.dev/<key> (or custom-domain) URL, available after you enable public access on the bucket.

One-time Cloudflare setup

  1. Account ID — Cloudflare dashboard → R2 → copy the Account ID.
  2. Create a bucket — dashboard → R2 → Create bucket, or wrangler r2 bucket create <name>.
  3. API token — dashboard → R2 → Manage R2 API TokensCreate API Token with Object Read & Write. Copy the Access Key ID and Secret Access Key.
  4. (public links only) bucket → Settings → enable Public Development URL, and copy the pub-xxxx.r2.dev domain.

Install

From PyPI:

uv tool install rink     # install as a CLI on your PATH (recommended)
uv pip install rink      # or into the active environment
pip install rink         # or with plain pip

From source (for development):

uv sync                 # install deps into the project venv
uv run rink --help      # run from the project
uv tool install .       # install this checkout as a tool on your PATH

Configure

rink config             # interactive wizard

Writes ~/.config/rink/config.toml (mode 600). Every field can also be supplied via environment variables, which override the file: RINK_ACCOUNT_ID, RINK_ACCESS_KEY_ID, RINK_SECRET_ACCESS_KEY, RINK_BUCKET, RINK_PUBLIC_BASE_URL.

Buckets

rink buckets            # list all buckets in the account (default is marked ●)
rink use                # interactive picker to choose the default bucket
rink use my-bucket      # set the default bucket directly

rink up --bucket <name> still overrides the bucket for a single upload without changing the default.

Usage

rink up ./report.pdf                  # presigned link (default expiry)
rink up ./report.pdf --expiry 1d      # 1-day presigned link (30m, 2h, 7d, 1h30m…)
rink up ./report.pdf --public         # permanent public link
rink up ./report.pdf -c               # also copy the link to the clipboard
rink up ./report.pdf --qr             # also print a scannable QR code
rink up ./mydir                       # zip the folder, one link (default)
rink up ./mydir --recursive           # upload each file in parallel, link per file
rink up *.png a.txt                   # multiple paths at once
cat dump.sql | rink up - --name db.sql  # upload from stdin
rink up ./big.bin --prefix backups/   # store under a key prefix
rink up ./secret.pdf --random         # unguessable key prefix
rink up ./report.pdf --download       # browser downloads instead of rendering
rink up ./report.pdf --quiet          # print only the URL (for scripts / pipes)
rink up ./report.pdf --json           # machine-readable output

Options:

flag default meaning
--public / --presigned --presigned link type
--expiry <dur> config default_expiry presigned lifetime: 30m, 2h, 7d, 1h30m, or bare seconds (max 7d)
--zip / --recursive --zip folder handling (recursive uploads in parallel)
--prefix <str> none key prefix in the bucket
--bucket <name> configured bucket override target bucket
--name <str> source filename object name (single upload; required for stdin)
--random off prepend a random token to the key (unguessable links)
--download off set Content-Disposition: attachment
--copy / -c off copy link(s) to the clipboard
--qr off print a QR code (single upload)
--quiet / -q off print only the URL(s)
--json off print results as JSON
--workers <n> 4 parallel uploads for recursive folders

Large files (≥ 8 MiB) upload via multipart automatically with a progress bar. The URL(s) are printed on their own line(s) so they're easy to copy or pipe. Use - as a path to read from stdin (with --name).

Listing, expiry, and deleting

rink ls                 # list objects in the bucket with each link's time-left
rink ls backups/        # only keys under a prefix
rink ls --expired       # only entries whose presigned link has already expired
rink link myfile.zip    # regenerate a fresh link for an existing object (no re-upload)
rink link myfile.zip --expiry 7d -c   # …with a new expiry, copied to clipboard
rink open myfile.zip    # open the object's link in your browser
rink rm myfile.zip      # delete an object (this is how you revoke access)
rink rm a.txt b.txt -y  # delete several, skip the confirmation
rink prune              # delete objects whose presigned link has expired

rink ls reads the live bucket and joins it with a local log to show the link column:

  • 2h / 1d 3h — time left on the presigned link
  • permanent — shared via a public link (never expires)
  • untracked — object exists in the bucket but wasn't uploaded by rink, so we don't know its link expiry

Why a local log? R2 stores files, not links. A presigned URL's expiry is baked into the URL string at generation time — R2 keeps no record of it. So rink logs each upload to a small SQLite database at ~/.local/share/rink/rink.db (stdlib, no extra deps) to report time-left. Deleting with rink rm removes both the object and its log row.

Note: the file itself never expires — only the share link does. To make a file unreachable, delete it with rink rm. (R2 has no per-object private/public switch; publicity is bucket-wide, so deletion is the way to revoke.)

Development

uv sync --dev      # install with dev dependencies
uv run pytest -q   # run the test suite (uses moto to mock S3; no network)

Tests live in tests/. GitHub Actions runs them on every push/PR across Python 3.10–3.13.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rink-0.2.5.tar.gz (16.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rink-0.2.5-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

Details for the file rink-0.2.5.tar.gz.

File metadata

  • Download URL: rink-0.2.5.tar.gz
  • Upload date:
  • Size: 16.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rink-0.2.5.tar.gz
Algorithm Hash digest
SHA256 8ae57c06b118e5a7cfea85c812e20d91b88fcbc354d23ea5a8252bb735fce9c9
MD5 0ab4cdf2f135cb852bc18fbafd1993f9
BLAKE2b-256 1d99f38e62f8371feb3bd252b46f54aff4b1b22a3ad10e570f1f058666d8a7ce

See more details on using hashes here.

File details

Details for the file rink-0.2.5-py3-none-any.whl.

File metadata

  • Download URL: rink-0.2.5-py3-none-any.whl
  • Upload date:
  • Size: 20.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rink-0.2.5-py3-none-any.whl
Algorithm Hash digest
SHA256 1e33bfdc8158502225a21fdb2a9223aa84d3fdc47360f13af186d95a95107016
MD5 cf345b5b6a3a73b8dc84a3fd7947ade6
BLAKE2b-256 c69bcc34a05bc093045ea67f16f05fbae09a66fac97c8bb3b9589d733c684862

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page