Cloudsteading
Analytics

Sink

Cloudflare short links with analytics, QR codes and routing controls.

Sink is a self-hosted Bitly/Rebrandly alternative built on Cloudflare (Analytics Engine, D1, KV, R2, Workers). Free tier eligible within limits. Inspect the source and license in the linked repository.

Source & license

Upstream license: AGPL-3.0-only

License TL;DR

You can use and change it, even commercially. If people use your modified version over a network, offer them its corresponding source under the AGPL. Sharing copies has source-sharing duties too. Sharing source code is different from sharing users’ content.

Explain AGPL v3 in plain English →

Summary of the main license. Separate packages and assets can have different terms.

Inspect repository ↗Read this project’s actual license ↗

Repository owner

@miantiao-me

See the upstream repository for the original creator and contributors.

Maintain this project? Maintainer verification →

Cloudflare hosting

Free tier eligible within limits

The reviewed Cloudflare deployment is eligible for Free-plan allowances for the stated small workload and feature scope. Usage limits, CPU, required account setup and separate services apply.

Hosting requirements
  • Replace committed resource IDs using the documented DEPLOY configuration and configure a private NUXT_SITE_TOKEN.
  • Keep dynamic requests/CPU, D1 row/storage and KV read/write quotas within Workers Free limits; links are not limited only by the request allowance.
  • Analytics Engine Free currently includes 100,000 written points/day and 10,000 read queries/day; future billing/pricing must be rechecked.
  • If optional AI is used, stay below 10,000 Neurons/day; if R2 snapshots are enabled, include retention in Standard R2 storage/operation quotas.
  • Exclude the docs Worker from app topology; AI and backup storage are optional features and may add costs when enabled beyond quotas.
  • Workers Free dynamic requests are shared across this account (100,000/day), with 10 ms CPU per invocation; workload fit is conditional and has not been measured.
  • D1 Free allowance: 5 million rows read/day, 100,000 rows written/day and 5 GB total storage; unindexed scans and history retention consume quota.
  • KV Free allowance: 100,000 keys read/day and 1,000 each writes/deletes/list requests/day; this may constrain updates before Worker request limits.
  • R2 Standard allowance: 10 GB-month storage, 1 million Class A and 10 million Class B operations/month; account activation may require billing setup, and other storage classes are excluded.
Check current pricing ↗
Sources checked 01/10/2026

Repository snapshot: bafa4e1. Hosting eligibility reflects the deployment documentation and listed assumptions.

  • bitly ↗

    o/badge/License-AGPL--3.0-blue?style=flat) ![Hero](./public/image.png) --- ## ✨ Features - **🔗 URL Shortening:** Compress your URLs to their minimal length. - **📈 Analytics:** Monitor link analytics and gather insightful statistics. - **☁️ Serverless:** Deploy without the need for traditional servers. - **🎨 Customizable Slug:** Support personalized slugs, UTM parameters, and optional case-sensitive slug matching through configuration. - **🪄 AI Assistance:** Optionally use Cloudflare Workers AI to generate slugs and OpenGraph metadata from page content. - *

  • rebrandly ↗

    ge content. - **⏰ Link Control:** Set expirations, passwords, and unsafe-link warning pages. - **📱 Smart Routing:** Redirect visitors by device or country. - **🖼️ Social Preview:** Customize social previews with titles, descriptions, and images. - **📊 Near-real-time Analytics:** Display a live 3D globe and event logs using 10-second analytics polling and client-side replay, not SSE or WebSocket. - **🔲 QR Code:** Generate QR codes for your short links. - **📦 Import/Export:** Transfer links via JSON and export access analytics via CSV. - **🌍 Multi-language:**

  • workers ↗

    { "$schema": "node_modules/wrangler/config-schema.json", "name": "sink", "main": ".output/server/index.mjs", "assets": { "binding": "ASSETS", "directory": ".output/public", "html_handling": "drop-trailing-slash" }, "compatibility_date": "2025-05-08", "compatibility_flags": [ "nodejs_compat" ], "keep_vars": true, "upload_source_maps": true, "observability": { "logs": { "enabled": true }, "traces": { "enabled": true } }, "ai": { "binding": "AI", "remote": true }, //

  • d1 ↗

    dd3" // Override with DEPLOY_KV_NAMESPACE_ID } ], "d1_databases": [ { "binding": "DB", "database_name": "sink", // Override with DEPLOY_D1_DATABASE_NAME (default: sink) "database_id": "ef964055-65bf-4b78-b109-ac9c9290ae62", // Override with DEPLOY_D1_DATABASE_ID (required for CLI deploy) "migrations_dir": "drizzle" } ], "r2_buckets": [ { "binding": "R2", // preview_bucket_name: DEPLOY_R2_PREVIEW_BUCKET_NAME (falls back to DEPLOY_R2_BUCKET_NAME) // omit DEPLOY_R2_BUCKET_NAME to drop R2 from generated

  • kv ↗

    Keep in sync with NUXT_DATASET at runtime. } ], "kv_namespaces": [ { "binding": "KV", "preview_id": "ef93d42dc4b34969bab404d2e80f8dd3", // Override with DEPLOY_KV_PREVIEW_NAMESPACE_ID (falls back to DEPLOY_KV_NAMESPACE_ID) "id": "ef93d42dc4b34969bab404d2e80f8dd3" // Override with DEPLOY_KV_NAMESPACE_ID } ], "d1_databases": [ { "binding": "DB", "database_name": "sink", // Override with DEPLOY_D1_DATABASE_NAME (default: sink) "database_id": "ef964055-65bf-4b78-b109-ac9c9290ae62", // Override with DEPLOY_D1

  • r2 ↗

    CLI deploy) "migrations_dir": "drizzle" } ], "r2_buckets": [ { "binding": "R2", // preview_bucket_name: DEPLOY_R2_PREVIEW_BUCKET_NAME (falls back to DEPLOY_R2_BUCKET_NAME) // omit DEPLOY_R2_BUCKET_NAME to drop R2 from generated deploy config "preview_bucket_name": "sink", "bucket_name": "sink" // Override with DEPLOY_R2_BUCKET_NAME } ], "triggers": { "crons": ["0 0 * * *"] // Run backup task daily at 00:00 UTC } }

  • workers-ai ↗

    "enabled": true }, "traces": { "enabled": true } }, "ai": { "binding": "AI", "remote": true }, // Values marked with DEPLOY_* below are placeholders for local/dev. // CLI deploy (pnpm deploy:worker / deploy:config) rewrites them into gitignored wrangler.deploy.jsonc. // Prefer setting DEPLOY_* in .env or Workers Builds / Pages variables — do not hardcode production IDs here. "analytics_engine_datasets": [ { "binding": "ANALYTICS", "dataset": "sink" // Override with DEPLOY_ANALYTICS_DATASET (default: sink).

  • analytics-engine ↗

    les — do not hardcode production IDs here. "analytics_engine_datasets": [ { "binding": "ANALYTICS", "dataset": "sink" // Override with DEPLOY_ANALYTICS_DATASET (default: sink). Keep in sync with NUXT_DATASET at runtime. } ], "kv_namespaces": [ { "binding": "KV", "preview_id": "ef93d42dc4b34969bab404d2e80f8dd3", // Override with DEPLOY_KV_PREVIEW_NAMESPACE_ID (falls back to DEPLOY_KV_NAMESPACE_ID) "id": "ef93d42dc4b34969bab404d2e80f8dd3" // Override with DEPLOY_KV_NAMESPACE_ID } ], "d1_databases": [ {

  • free-tier-eligible ↗

    { "$schema": "node_modules/wrangler/config-schema.json", "name": "sink", "main": ".output/server/index.mjs", "assets": { "binding": "ASSETS", "directory": ".output/public", "html_handling": "drop-trailing-slash" }, "compatibility_date": "2025-05-08", "compatibility_flags": [ "nodejs_compat" ], "keep_vars": true, "upload_source_maps": true, "observability": { "logs": { "enabled": true }, "traces": { "enabled": true } }, "ai": { "binding": "AI", "remot

  • free-tier-eligible ↗

    ount Manager. | | Requests<sup>1, 2, 3, 4</sup> | Duration | CPU time | | --- | --- | --- | --- | | **Free** | 100,000 per day | No charge for duration | 10 milliseconds of CPU time per invocation | | **Standard** | 10 million included per month <br> +$0.30 per additional million | No charge or limit for duration | 30 million CPU milliseconds included per month<br> +$0.02 per additional million CPU milliseconds<br><br> Max of [5 minutes of CPU time](https://developers.cloudflare.com/workers/platform/limits/#account-plan-limits) per invocation (default: 30 second

  • free-tier-eligible ↗

    rs Paid](https://developers.cloudflare.com/workers/platform/pricing/#workers) | | --- | --- | --- | | Rows read | 5 million / day | First 25 billion / month included + $0.001 / million rows | | Rows written | 100,000 / day | First 50 million / month included + $1.00 / million rows | | Storage (per GB stored) | 5 GB (total) | First 5 GB included + $0.75 / GB-mo | Track your D1 usage To accurately track your usage, use the [meta object](https://developers.cloudflare.com/d1/worker-api/return-object/), [GraphQL Analytics API](https://developers.cloudflare.com/d1/obs

  • free-tier-eligible ↗

    flare.com/workers/platform/pricing/). | | Free plan<sup>1</sup> | Paid plan | | --- | --- | --- | | Keys read | 100,000 / day | 10 million/month, + $0.50/million | | Keys written | 1,000 / day | 1 million/month, + $5.00/million | | Keys deleted | 1,000 / day | 1 million/month, + $5.00/million | | List requests | 1,000 / day | 1 million/month, + $5.00/million | | Stored data | 1 GB | 1 GB, + $0.50/ GB-month | <sup>1</sup> The Workers Free plan includes limited Workers KV usage. All limits reset daily at 00:00 UTC. If you exceed any one of these limits, further o

  • free-tier-eligible ↗

    f you have retrieved data (for infrequent access storage) for 1.1 GB, you will be billed for 2 GB. ### Free tier You can use the following amount of storage and operations each month for free. | | Free | | --- | --- | | Storage | 10 GB-month / month | | Class A Operations | 1 million requests / month | | Class B Operations | 10 million requests / month | | Egress (data transfer to Internet) | Free <sup>[1](#user-content-fn-1)</sup> | Caution The free tier only applies to Standard storage, and does not apply to Infrequent Access storage. ### Storage usage S

  • free-tier-eligible ↗

    evelopers.cloudflare.com/workers/platform/pricing/) and is priced at **$0.011 per 1,000 Neurons**. Our free allocation allows anyone to use a total of **10,000 Neurons per day at no charge**. To use more than 10,000 Neurons per day, you need to sign up for the [Workers Paid plan](https://developers.cloudflare.com/workers/platform/pricing/#workers). On Workers Paid, you will be charged at $0.011 / 1,000 Neurons for any usage above the free allocation of 10,000 Neurons per day. You can monitor your Neuron usage in the [Cloudflare Workers AI dashboard ↗︎](https://d

  • free-tier-eligible ↗

    r> (+$0.25 per additional million) | 1 million included per month (+$1.00 per additional million) | | **Workers Free** | 100,000 included per day | 10,000 included per day | Pricing availability Currently, you will not be billed for your use of Workers Analytics Engine. Pricing information here is shared in advance, so that you can estimate what your costs will be once Cloudflare starts billing for usage in the coming months. If you are an Enterprise customer, contact your account team for information about Workers Analytics Engine pricing and billing. ### Dat

  • AGPL-3.0-only ↗

    GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software.

  • architecture ↗

    { "$schema": "node_modules/wrangler/config-schema.json", "name": "sink", "main": ".output/server/index.mjs", "assets": { "binding": "ASSETS", "directory": ".output/public", "html_handling": "drop-trailing-slash" }, "compatibility_date": "2025-05-08", "compatibility_flags": [ "nodejs_compat" ], "keep_vars": true, "upload_source_maps": true, "observability": { "logs": { "enabled": true }, "traces": { "enabled": true } }, "ai": { "binding": "AI", "remote": true }, //

Upstream screenshot · miantiao-me/Sink repository contributors ↗. Depicts the upstream project. We have not deployed and tested a fresh installation here.

What it can replace

Compare the workflow you need. These mappings describe overlap; full feature parity requires a separate comparison.

Bitly logoBitly ↗

Editorial workflow alternative: Short URLs, custom slugs, QR codes and link analytics for individuals/small teams; no managed SLA or multi-user parity.

See supporting source ↗
Rebrandly logoRebrandly ↗

Editorial workflow alternative: Personal short-link management, analytics and device/country routing; branded-domain portfolio and team-workflow parity are not verified.

See supporting source ↗
external SaaS target
varies
external SaaS target
varies

How it works

The shape of Sink on Cloudflare, and how it stacks up against the rented tools it replaces.

Architecture

Diagram of deployment declarations at the reviewed commit. Each app has its own entrypoint; declared resources do not prove runtime calls. Follow file and line sources below.

View upstream source ↗
Public interface
Configured entry points1
sink
wrangler.jsonc
↓
App
sink
entry
Cloudflare Workers
Entrypoint: .output/server/index.mjsConfigured cron (UTC): 0 0 * * *
↓

Configuration and workflow sources

Reviewed commit bafa4e1f7e3e. Files were read as data; upstream applications and CI jobs were not executed.

Deployment configuration · 2 files
wrangler.jsonc ↗

Cloudflare Workers · compatibility 2025-05-08

sink · default

Entrypoint: .output/server/index.mjs

Static assets: .output/public

Cron triggers (UTC): 0 0 * * *

  • DB → D1
  • KV → KV
  • R2 → R2
  • AI → Workers AI
  • ANALYTICS → Analytics Engine
  • ASSETS → Static assets
docs/wrangler.jsonc ↗

Cloudflare Workers · example/template, excluded from overview · compatibility 2026-07-12

sink-docs · default

Static assets: ./.vitepress/dist · 404-page

  • Static assets → Static assets

Named environments are separate deployments. Bindings are shown only where declared. Configured routes are URL patterns, not verified application endpoints.

Runtime source · handlers, binding usage and workflow steps

Observed TypeScript/JavaScript declarations from Worker entrypoints and resolved relative imports. Calls and workflow steps may run conditionally; their listed order is not a proven end-to-end request flow. Router declarations may be mounted under a prefix or may not be registered. This shows code wiring, not a successful deployment or runtime test. Dynamic wiring, aliases and generated code may not resolve.

No direct runtime declarations resolved from this snapshot. Generated framework bundles or dynamic entrypoints need manual tracing.

Build and deployment pipeline · 1 GitHub Actions workflows

Repository CI declarations, separate from runtime request processing. Job dependencies and conditions are shown as written; long commands are shortened with an ellipsis; a workflow file does not prove a recent successful run.

issue-assistant · .github/workflows/issue-assistant.yml ↗

Triggers: issues

issue-assistant · no job dependencies declared

  1. Checkout repositoryactions/checkout@v4
  2. Set up Bunoven-sh/setup-bun@v2
  3. Install opencodecurl -fsSL https://opencode.ai/install | bash
  4. Analyze issueopencode run -m ${{ secrets.OPENCODE_MODEL }} "A new issue #${{ github.event.issue.number }} has been created. Lookup this issue, then do the following: 1. Read README.md and all files under docs/ directory to see if any documentation answers the issue. 2. Search existing issues (excluding #${{ github.event.issue.number }}) for potential duplicates by similarity in title, description, error messages, or functionality. Based on your findings, post AT MOST ONE comment on #${{ github.event.issue.n…
package.json ↗
  • build:docs: pnpm --filter @sink/docs build
  • prebuild: pnpm build:sphere
  • build: NODE_OPTIONS=--max-old-space-size=8192 nuxt build
  • postbuild: test "$CF_PAGES:$CF_PAGES_BRANCH" != "1:master" || pnpm db:migrate:remote
  • build:map: node scripts/build-map.js
  • build:sphere: node scripts/build-sphere.js
  • build:colo: node scripts/build-colo.js
  • build:testimonials: node scripts/build-testimonials.js
  • deploy:config: node scripts/cloudflare-deploy-config.mjs
  • deploy: pnpm deploy:pages
  • deploy:pages: pnpm db:migrate:remote && wrangler pages deploy dist
  • deploy:worker: pnpm db:migrate:remote && wrangler deploy .output/server/index.mjs --assets .output/public --config wrangler.deploy.jsonc
docs/package.json ↗
  • build: vitepress build .
  • deploy: pnpm build && wrangler deploy --config ./wrangler.jsonc

Full upstream document by @miantiao-me · README.md · snapshot bafa4e1

⚡ Sink

A Simple, Speedy, Secure, and Serverless Link Shortener with Analytics, Running Entirely on Cloudflare.

Website · Documentation · API Reference

miantiao-me/Sink | Trendshift Featured on Hacker News Featured|HelloGitHub Uneed Badge

DeepWiki Cloudflare Nuxt Tailwind CSS shadcn/ui License

Hero


✨ Features

  • 🔗 URL Shortening: Compress your URLs to their minimal length.
  • 📈 Analytics: Monitor link analytics and gather insightful statistics.
  • ☁️ Serverless: Deploy without the need for traditional servers.
  • 🎨 Customizable Slug: Support personalized slugs, UTM parameters, and optional case-sensitive slug matching through configuration.
  • 🪄 AI Assistance: Optionally use Cloudflare Workers AI to generate slugs and OpenGraph metadata from page content.
  • ⏰ Link Control: Set expirations, passwords, and unsafe-link warning pages.
  • 📱 Smart Routing: Redirect visitors by device or country.
  • 🖼️ Social Preview: Customize social previews with titles, descriptions, and images.
  • 📊 Near-real-time Analytics: Display a live 3D globe and event logs using 10-second analytics polling and client-side replay, not SSE or WebSocket.
  • 🔲 QR Code: Generate QR codes for your short links.
  • 📦 Import/Export: Transfer links via JSON and export access analytics via CSV.
  • 🌍 Multi-language: Full i18n support for dashboard and redirect pages.

[!TIP] Who is Sink for?

Sink focuses on individuals and small teams who want a simple, self-hosted shortener on Cloudflare.

For professional / business needs (managed service, multi-user, SLA, and more), use S.EE.

🪧 Demo

Experience the demo at Sink.Cool. Log in using the Site Token below:

Site Token: SinkCool
Screenshots Analytics Links Link Analytics

🔀 Sibling versions

Sink and Slite are sibling versions of the same link-management and analytics project. Sink runs on Cloudflare's serverless platform, while Slite runs as a local Node.js 24+/Docker process. They keep features, API contracts, and file organization compatible with each other wherever practical. Neither version is a legacy branch, and Slite is not a fork replacement for Sink.

🧱 Technologies Used

🚗 Roadmap [WIP]

We welcome your contributions and PRs.

  • Browser Extension - Sink Tool
  • Chrome Extension - Sink Quick Shorten
  • Raycast Extension - Raycast-Sink
  • Apple Shortcuts - Sink Shortcuts
  • iOS App - Sink
  • Enhanced Link Management (with Cloudflare D1)
  • Analytics Enhancements (Multi-link filtering)
  • Dashboard Performance Optimization (Infinite loading)
  • API, migration, backup, and redirect tests

🏗️ Deployment

Video tutorial: Watch here

We currently support deployment to Cloudflare Workers (recommended) and Cloudflare Pages (deprecated).

⚒️ Configuration

Configuration Docs

🔌 API

API Docs · Live Scalar Reference for the public demo instance

🤖 AI Skills

Install Sink AI Skills for enhanced coding assistance:

npx skills add miantiao-me/sink

🧰 MCP

Sink serves a built-in MCP endpoint at POST /api/mcp, using the official @modelcontextprotocol/server SDK v2, serving modern clients over the per-request transport and 2025-era clients over a stateless fallback with JSON responses.

Replace the domain below with your own instance, and use the NUXT_SITE_TOKEN from your instance's environment variables as the bearer token.

claude mcp add --transport http sink https://sink.cool/api/mcp --header "Authorization: Bearer SinkCool"

Any client that supports an HTTP transport with custom headers can connect the same way:

{
  "mcpServers": {
    "sink": {
      "type": "http",
      "url": "https://sink.cool/api/mcp",
      "headers": {
        "Authorization": "Bearer SinkCool"
      }
    }
  }
}

It exposes tools for managing links (list, search, read, count, tag, create, update, upsert, delete) and for reading analytics (counters, views over time, and top values per dimension). See the integrations documentation for the full list.

🙋🏻 FAQs

FAQs

💖 Credits

  1. Cloudflare
  2. NuxtHub
  3. Astroship
  4. Tailark

📄 License

AGPL-3.0-only © miantiao-me

☕ Sponsor

  1. Follow Me on X (Twitter).
  2. Become a sponsor on GitHub.

Frequently asked about Sink

What is Sink?+

Sink is a self-hosted Bitly/Rebrandly alternative built on the Cloudflare developer platform. Cloudflare short links with analytics, QR codes and routing controls.

What does Sink replace?+

Sink is listed as an alternative to Bitly, Rebrandly. Compare the features and tradeoffs before migrating.

What Cloudflare primitives does Sink use?+

Sink is built on Analytics Engine, D1, KV, R2, Workers, Workers AI.

How much does Sink cost to run?+

The reviewed Cloudflare deployment is eligible for Free-plan allowances for the stated small workload and feature scope. Usage limits, CPU, required account setup and separate services apply. Replace committed resource IDs using the documented DEPLOY configuration and configure a private NUXT_SITE_TOKEN. Keep dynamic requests/CPU, D1 row/storage and KV read/write quotas within Workers Free limits; links are not limited only by the request allowance. Analytics Engine Free currently includes 100,000 written points/day and 10,000 read queries/day; future billing/pricing must be rechecked. If optional AI is used, stay below 10,000 Neurons/day; if R2 snapshots are enabled, include retention in Standard R2 storage/operation quotas. Exclude the docs Worker from app topology; AI and backup storage are optional features and may add costs when enabled beyond quotas. Workers Free dynamic requests are shared across this account (100,000/day), with 10 ms CPU per invocation; workload fit is conditional and has not been measured. D1 Free allowance: 5 million rows read/day, 100,000 rows written/day and 5 GB total storage; unindexed scans and history retention consume quota. KV Free allowance: 100,000 keys read/day and 1,000 each writes/deletes/list requests/day; this may constrain updates before Worker request limits. R2 Standard allowance: 10 GB-month storage, 1 million Class A and 10 million Class B operations/month; account activation may require billing setup, and other storage classes are excluded. Check current Cloudflare pricing before deploying.

Is Sink open source?+

The upstream repository declares the AGPL-3.0-only license. Read its terms at https://raw.githubusercontent.com/miantiao-me/Sink/bafa4e1f7e3ec034929e11e5542c4c3e116d69da/LICENSE. Source code and contributor credit are available at https://github.com/miantiao-me/Sink.

Discussion · 0

sign in to comment →
No comments yet — be the first.