Skip to main content

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

Project description

rink

A small CLI that uploads a file or folder to a Cloudflare R2 bucket and prints a shareable link. R2 is S3-compatible, so rink talks to it with boto3.

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 86400   # 1-day presigned link
rink up ./report.pdf --public         # permanent public link
rink up ./mydir                       # zip the folder, one link (default)
rink up ./mydir --recursive           # upload each file, one link per file
rink up ./big.bin --prefix backups/   # store under a key prefix
rink up ./f.txt --bucket other-bucket # override the configured bucket

Options:

flag default meaning
--public / --presigned --presigned link type
--expiry <sec> config default_expiry presigned lifetime (≤ 604800)
--zip / --recursive --zip folder handling
--prefix <str> none key prefix in the bucket
--bucket <name> configured bucket override target bucket

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.

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 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 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.)

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.1.1.tar.gz (11.5 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.1.1-py3-none-any.whl (14.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: rink-0.1.1.tar.gz
  • Upload date:
  • Size: 11.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.14

File hashes

Hashes for rink-0.1.1.tar.gz
Algorithm Hash digest
SHA256 15ab00d4443d7c7175523ae14e089068881226bc9f8024fa0c551ec5a6bdf702
MD5 f5b09c71335585495c8262d91150f9f8
BLAKE2b-256 dafa6e873c954fe304eb2482b2ccbd459e7d00e88c2c6e4da282ed79d11bb2f9

See more details on using hashes here.

File details

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

File metadata

  • Download URL: rink-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 14.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.14

File hashes

Hashes for rink-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 eb312a2ad2a4baf5cac2b7660829acf4265dfe165bf5fe0a9166f3543a441902
MD5 5d8344afc365bc72457b09d3fc49e63d
BLAKE2b-256 f7f8ff76c71870e5b98d98b3f126ff85a1e338cfd9116a4abe17ec907e38e53c

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