Skip to main content

share

CLI file sharing and link shortening with download tracking. Backed by Cloudflare R2 + Workers KV + Workers. Zero cost, full ownership.

Live at icecube.to · 🧊.to

Why

Google Drive has no CLI. transfer.sh is dead. Presigned S3 URLs expire. This uploads a file or shortens a URL, gives you a short link, copies it to clipboard. Done.

$ share upload README.md --slug readme.md --public
https://icecube.to/readme.md (copied)

$ share link https://github.com/adriangalilea/share --slug gh --public
https://icecube.to/gh → https://github.com/adriangalilea/share (copied)

$ share ls
                              Files
┏━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━┳━━━━━┓
┃ Slug      ┃ Name      ┃     Size ┃ Uploaded   ┃ DLs ┃ Vis ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━╇━━━━━┩
│ readme.md │ README.md │   7.2 KB │ 2026-03-19 │  15 │ pub │
└───────────┴───────────┴──────────┴────────────┴─────┴─────┘
                              Links
┏━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━┳━━━━━┓
┃ Slug ┃ URL                                       ┃ Created    ┃ Clicks ┃ Vis ┃
┡━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━╇━━━━━┩
│ gh   │ https://github.com/adriangalilea/share     │ 2026-03-19 │      2 │ pub │
└──────┴───────────────────────────────────────────┴────────────┴────────┴─────┘
icecube.to/<slug>

$ share rm gh
Deleted https://github.com/adriangalilea/share (/gh)

This README is shared at icecube.to/readme.md.

Usage

share upload <file>                         # upload file, auto-generate slug
share upload <file> --slug <name>           # custom slug
share upload <file> --public                # show on landing page (default: private)
share upload <file> --name <name>           # override download filename
share upload <file> --keep-metadata         # skip EXIF/metadata stripping
share link <url>                            # shorten a URL
share link <url> --slug <name>              # custom slug for short link
share link <url> --public                   # show on landing page
share ls                                    # list all files and links
share rm <slug>                             # delete by slug, filename, URL, or r2_key
share setup                                 # interactive first-time config

Uploads strip EXIF/metadata by default (images via Pillow, videos via ffmpeg).

Install

uv tool install git+https://github.com/adriangalilea/share.git

Architecture

CLI (Python)                        Cloudflare Free Tier
┌──────────────┐    boto3/S3 API    ┌─────────────────┐
│  share CLI   │ ─────────────────→ │       R2        │ files (10GB free)
│              │                    └────────┬────────┘
│              │    CF Python SDK           │
│              │ ─────────────────→ ┌────────┴────────┐
│              │                    │       KV        │ slug → metadata
└──────────────┘                    └────────┬────────┘
                                            │
Browser                                     │
┌──────────────┐                    ┌────────┴────────┐
│  short link  │ ─────────────────→ │     Worker      │ serves files,
│              │                    │ (custom domain) │ tracks downloads,
└──────────────┘                    └─────────────────┘ landing page

The CLI uploads files to R2 and writes metadata to KV keyed by slug:<slug>. The Worker on your custom domain looks up slugs, streams files from R2 with range request support (for video/audio streaming), and increments download counters.

Every response carries a Content-Disposition header with the original filename so downloads keep their extension regardless of browser or OS (the slug in the URL has no extension; without this header some browsers save the file extensionless). Previewable types — text/*, anything containing json/xml/javascript, image/*, audio/*, video/*, and application/pdf — are served inline so the link still renders in-tab. Everything else (all archives: .7z, .zip, .tar, .gz, .rar, …; binaries; unknown application/octet-stream) is served attachment and downloads with the correct name.

Self-hosting

Prerequisites

  • Python >=3.12, uv
  • Cloudflare account (free tier)
  • Node.js (for worker deployment)
  • A domain pointed to Cloudflare nameservers

1. Install the CLI

uv tool install git+https://github.com/adriangalilea/share.git

2. Create Cloudflare resources

You need two API tokens:

R2 API token (S3-compatible, for file uploads):

  • Cloudflare dashboard → R2 → Manage R2 API Tokens → Create
  • Permissions: Object Read & Write
  • Save the Access Key ID + Secret

CF API token (for KV + worker deployment):

  • My Profile → API Tokens → Create Token
  • Use template: Edit Cloudflare Workers
  • Add permissions: Zone DNS Edit + Zone Read
  • Save the token

3. Run setup

share setup

This verifies credentials, creates the R2 bucket and KV namespace, and writes config to ~/.config/share/config.toml.

4. Configure and deploy the worker

Clone the repo and edit worker/wrangler.toml:

name = "share"
main = "src/index.ts"
compatibility_date = "2026-03-19"

[[r2_buckets]]
binding = "R2"
bucket_name = "share"

[[kv_namespaces]]
binding = "KV"
id = "your-kv-namespace-id"  # printed by share setup

[[routes]]
pattern = "yourdomain.com"
custom_domain = true

[vars]
SITE_NAME = "yourdomain.com"

Then deploy:

cd worker
npx wrangler deploy

5. Add your domain to Cloudflare

If not already done:

  1. Cloudflare dashboard → Add a site → your domain
  2. Update nameservers at your registrar to the ones Cloudflare gives you
  3. The worker deploy will create the DNS records automatically
  4. SSL/TLS → Edge Certificates → Always Use HTTPS → ON

6. Upload

share upload myfile.pdf
# https://yourdomain.com/kX9mT (copied)

Config

~/.config/share/config.toml — see config.example.toml.

Key Description
cloudflare.account_id Cloudflare account ID
cloudflare.r2_access_key_id R2 S3 API access key
cloudflare.r2_secret_access_key R2 S3 API secret key
cloudflare.api_token CF API token
cloudflare.bucket R2 bucket name (default: share)
cloudflare.kv_namespace_id Workers KV namespace ID
urls.public_base Your domain (e.g. https://yourdomain.com)
upload.strip_metadata Strip EXIF before upload (default: true)

Cloudflare free tier limits

Service Limit Usage
R2 10 GB storage, 1M writes/mo, 10M reads/mo, zero egress File storage
Workers KV 1 GB storage, 100K reads/day, 1K writes/day File metadata
Workers 100K requests/day, 10ms CPU/request Serve files + landing page

VPN note

R2's S3 endpoint uses TLS that some VPNs break. If uploads fail with SSL errors, bypass VPN for r2.cloudflarestorage.com.

KV schema

slug:<slug> → file: { type?, name, size, content_type, uploaded_at, downloads, r2_key, slug, public }
slug:<slug> → link: { type: "link", url, slug, created_at, clicks, public }

Download files

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

Source Distribution

share-0.5.5.tar.gz (15.7 kB view details)

Uploaded Source

Built Distribution

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

share-0.5.5-py3-none-any.whl (9.7 kB view details)

Uploaded Python 3

File details

Details for the file share-0.5.5.tar.gz.

File metadata

  • Download URL: share-0.5.5.tar.gz
  • Upload date:
  • Size: 15.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for share-0.5.5.tar.gz
Algorithm Hash digest
SHA256 1861496f3671cf292ebfcb226d678d6cef869d218e4d226124265814b504f59c
MD5 96b79564e3277c72c6316ed8993049c2
BLAKE2b-256 8aade88d81d15433975cbb1bbf4e8e501ec0247d5e09c3b4cdedb0252b6f4e9c

See more details on using hashes here.

Provenance

The following attestation bundles were made for share-0.5.5.tar.gz:

Publisher: release.yml on adriangalilea/share

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file share-0.5.5-py3-none-any.whl.

File metadata

  • Download URL: share-0.5.5-py3-none-any.whl
  • Upload date:
  • Size: 9.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for share-0.5.5-py3-none-any.whl
Algorithm Hash digest
SHA256 fc9c347c4462d64b8bac7600422be4d427418301b7edeb9b2e6e5f7cdc1dcd3e
MD5 338b34e3ad926c727e10af1d4b568107
BLAKE2b-256 a4dedad5cdd5dbf7f2b11e2d7dcc0e547b2d7d53b38a5eaa9f00783f54912097

See more details on using hashes here.

Provenance

The following attestation bundles were made for share-0.5.5-py3-none-any.whl:

Publisher: release.yml on adriangalilea/share

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.5.5 This release

2 files

0.5.4

2 files

0.5.3

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page