Upload files/folders to Cloudflare R2 and get a shareable link
Project description
rink
rink is a fast, terminal-first utility to upload files or directories directly to a Cloudflare R2 bucket and instantly get a shareable URL.
Cloudflare R2 objects are private by default, so rink provides two ways to share:
- Presigned Links (Default): A secure, signed URL that automatically expires (up to 7 days). Does not require public bucket access.
- Public Links: A permanent URL (
https://<pub-domain>/<key>) using your bucket's public access domain.
Features
- Direct Uploads: Single files, multiple files, folders, or piped input from standard input (
stdin). - Flexible Folder Handling: Zip directories automatically into a single file, or upload them recursively in parallel.
- Terminal Enhancements: Copy links directly to your clipboard (
-c) or display scannable QR codes (--qr). - Track Expirations: Uses a lightweight local SQLite database to track presigned URLs and their remaining lifespan.
- Multipart Uploads: Automatically switches to multipart uploads for files ≥ 8 MiB with smooth progress bars.
- Self-Cleaning: Easily list expired links and prune them from R2 to keep your storage clean.
Installation
Install rink via PyPI. We recommend using uv for easy tool management, but standard pip works too.
Using uv (Recommended)
# Install as a global CLI tool on your PATH
uv tool install rink
# Or install into your active environment
uv pip install rink
Using pip or pipx
pipx install rink
# or
pip install rink
From Source (Development)
# Clone the repository and sync dependencies
git clone https://github.com/HACKE-RC/rink.git
cd rink
uv sync
# Run from the project
uv run rink --help
# Install the local checkout as a global tool
uv tool install .
One-Time Cloudflare Setup
Before using rink, you need to retrieve your Cloudflare credentials and create a bucket:
- Get Account ID: Go to the Cloudflare Dashboard → R2 → Copy the Account ID from the right sidebar.
- Create a Bucket: Click Create bucket in the dashboard, or run
wrangler r2 bucket create <name>. - Generate API Token:
- Click Manage R2 API Tokens → Create API Token.
- Select permissions: Object Read & Write.
- Copy the Access Key ID and Secret Access Key.
- (Optional for Public Links): In your bucket's page → Settings → Public Access → Enable Public Development URL (or configure a custom domain) and copy the
pub-xxxx.r2.devaddress.
Quick Start
Initialize rink using the interactive setup wizard:
rink config
This wizard will prompt you for your Account ID, Access Keys, default bucket name, and preferred link configurations.
Configuration Storage
Your settings are saved at ~/.config/rink/config.toml (file mode 600 for security).
You can also use environment variables to configure rink or override config files:
RINK_ACCOUNT_IDRINK_ACCESS_KEY_IDRINK_SECRET_ACCESS_KEYRINK_BUCKETRINK_PUBLIC_BASE_URL
Usage & Command Reference
Uploading Files (rink up)
Upload one or more files/directories and print their shareable links.
# Upload a single file with default expiry (signed URL)
rink up report.pdf
# Upload and copy the link directly to clipboard
rink up report.pdf -c
# Generate a scannable terminal QR code for the upload
rink up report.pdf --qr
# Upload with a custom expiration (e.g. 30m, 2h, 7d, 1h30m, or bare seconds)
rink up report.pdf --expiry 1d
# Upload as a permanent public link (requires public URL setup)
rink up report.pdf --public
# Pipe content directly from stdin (requires --name)
cat logs.txt | rink up - --name system_logs.txt
# Prefix files or randomize the upload paths for privacy
rink up invoice.pdf --prefix backups/
rink up secret.docx --random
# Force browsers to download the file instead of viewing it inline
rink up photo.png --download
Upload Options
| Flag | Default | Description |
|---|---|---|
--public / --presigned |
--presigned |
Choose link type (public permanent or presigned temporary). |
--expiry <duration> |
Config default | Presigned URL duration (e.g. 30m, 2h, 7d). Max 7d. |
--zip / --recursive |
--zip |
Folders: zip into one file, or upload contents recursively in parallel. |
--workers <count> |
4 |
Number of parallel upload workers for recursive directories. |
--prefix <string> |
None | Object key prefix path in the bucket. |
--bucket <name> |
Config bucket | Override the target bucket. |
--name <string> |
Source filename | Destination key name (required for stdin). |
--random |
Off | Prepend a random string to the destination key for unguessable links. |
--download |
Off | Forces the browser to download the file (Content-Disposition: attachment). |
--copy, -c |
Off | Copies the generated URL(s) to the clipboard. |
--qr |
Off | Prints a terminal QR code for the uploaded file link. |
--quiet, -q |
Off | Prints only the URL(s) (useful for scripting and piping). |
--json |
Off | Formats command output as JSON. |
Listing & Managing Buckets
# List all R2 buckets in your Cloudflare account
rink buckets
# Pick a default bucket interactively
rink use
# Set default bucket directly
rink use my-bucket
Listing & Regenerating Links (rink ls, rink link, rink open)
# List all tracked uploads with their remaining lifetime
rink ls
# Filter tracked files by bucket prefix
rink ls backups/
# Filter to show only files with expired presigned links
rink ls --expired
# Regenerate a fresh URL for an existing file (without re-uploading)
rink link invoice.pdf --expiry 7d -c
# Open an uploaded file's URL in your default web browser
rink open invoice.pdf
Deleting & Revoking Access (rink rm, rink prune)
To revoke access to a file, you must delete it from the bucket.
# Delete an object and clear its tracking log
rink rm report.pdf
# Delete multiple files and skip verification prompts
rink rm image1.png image2.png -y
# Delete all R2 files whose tracked presigned links have expired
rink prune
How It Works (Under the Hood)
Since Cloudflare R2 stores raw object files rather than temporary share links, R2 has no native concept of "when a presigned link expires". A presigned URL's expiration is computed cryptographically and embedded directly in the URL query string.
To solve this, rink maintains a lightweight local SQLite database at ~/.local/share/rink/rink.db.
- Every time you perform an upload,
rinklogs the object key, bucket name, and link expiration timestamp locally. - Running
rink lsjoins live bucket object information with this local log to determine exactly how much time is left on each link. - Objects uploaded outside of
rink(or whose local log records are missing) will show up asuntracked. - Deleting an object via
rink rmdeletes it from R2 and removes its tracking entry from your database.
[!NOTE] Presigned link expiration does not delete the file from R2; it only makes the URL invalid. To save bucket space, run
rink pruneto delete expired files from your bucket.
Development
Contributions are welcome! To set up your local development environment:
- Clone the repository and install dev dependencies:
uv sync --dev
- Run tests (we use
mototo mock S3/R2 requests locally; no live network calls are made):uv run pytest -v
- Run tests quietly:
uv run pytest -q
License
This project is licensed under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rink-0.2.6.tar.gz.
File metadata
- Download URL: rink-0.2.6.tar.gz
- Upload date:
- Size: 17.7 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1d97f3491533d69f5ad9f21382c2e80ac28f2d6cc699485c609a46cd345c8c39
|
|
| MD5 |
3603b91082aca802fe2bae2591898d6b
|
|
| BLAKE2b-256 |
2125048104ccada51211d42effead6b3c7fc38a6baa951d871d6c3de979a0824
|
File details
Details for the file rink-0.2.6-py3-none-any.whl.
File metadata
- Download URL: rink-0.2.6-py3-none-any.whl
- Upload date:
- Size: 21.6 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a8d47280398af493fb25081cfec52e046d80353277427ee9e00c68689b4d8844
|
|
| MD5 |
1ecf2874440b2db8b0fc0b2f34cc7025
|
|
| BLAKE2b-256 |
d9eece19096fbf3e88ecbb8173a796c67229a0094bb58b367e1b6eb776f318a4
|