
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
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.
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)  --- ## ✨ 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.
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 ↗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 ↗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 ↗Configuration and workflow sources
Reviewed commit bafa4e1f7e3e. Files were read as data; upstream applications and CI jobs were not executed.
Deployment configuration · 2 files
Cloudflare Workers · compatibility 2025-05-08
sink · default
Entrypoint: .output/server/index.mjs
Static assets: .output/public
Cron triggers (UTC): 0 0 * * *
DB→ D1KV→ KVR2→ R2AI→ Workers AIANALYTICS→ Analytics EngineASSETS→ Static assets
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.
Triggers: issues
issue-assistant · no job dependencies declared
- Checkout repository
actions/checkout@v4 - Set up Bun
oven-sh/setup-bun@v2 - Install opencode
curl -fsSL https://opencode.ai/install | bash - Analyze issue
opencode 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…
build:docs: pnpm --filter @sink/docs buildprebuild: pnpm build:spherebuild: NODE_OPTIONS=--max-old-space-size=8192 nuxt buildpostbuild: test "$CF_PAGES:$CF_PAGES_BRANCH" != "1:master" || pnpm db:migrate:remotebuild:map: node scripts/build-map.jsbuild:sphere: node scripts/build-sphere.jsbuild:colo: node scripts/build-colo.jsbuild:testimonials: node scripts/build-testimonials.jsdeploy:config: node scripts/cloudflare-deploy-config.mjsdeploy: pnpm deploy:pagesdeploy:pages: pnpm db:migrate:remote && wrangler pages deploy distdeploy:worker: pnpm db:migrate:remote && wrangler deploy .output/server/index.mjs --assets .output/public --config wrangler.deploy.jsonc
build: vitepress build .deploy: pnpm build && wrangler deploy --config ./wrangler.jsonc
Repository README
View original on GitHub ↗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

✨ 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
🔀 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
- Framework: Nuxt 4
- Database: Cloudflare D1 is the authoritative link store; Workers KV is a write-through read cache
- ORM: Drizzle ORM
- Analytics Engine: Cloudflare Workers Analytics Engine
- Object Storage: Cloudflare R2 for optional logical JSON snapshots
- AI: Optional Cloudflare Workers AI
- UI Components: shadcn-vue
- Styling: Tailwind CSS
- Deployment: Cloudflare
🚗 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
🔌 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_TOKENfrom 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
💖 Credits
📄 License
☕ Sponsor
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 →