Skip to content

Latest commit

 

History

History
293 lines (199 loc) · 25.4 KB

File metadata and controls

293 lines (199 loc) · 25.4 KB
Sublink Worker

Sublink Worker

One Worker, All Subscriptions

A lightweight subscription converter and manager for proxy protocols, deployable on Cloudflare Workers, Vercel, Node.js, or Docker.

English · 简体中文

7Sageer%2Fsublink-worker | Trendshift

Deploy to Cloudflare Workers Deploy to Vercel

📚 Documentation

⚡ Live Demo · Documentation 中文文档·

Quick Start · API Reference · FAQ

🔱 About This Fork

This is a community fork of 7Sageer/sublink-worker maintained at WayneWu98/sublink-worker. All credit for the original project goes to the upstream authors.

Differences from upstream (at the time of writing):

  • Short-link token auth (v2.5+)/shorten-v2 returns {code, token}; overwriting an existing short code requires the matching X-Shortlink-Token header.

  • Short code loader UI + /resolve read auth (v2.6+) — Explicit "Load from Code" button replaces the removed paste-auto-parse flow; /resolve now authenticates new-format entries.

  • Surge #!MANAGED-CONFIG short-URL preservation (v2.7+) — Surge configs fetched via a short link embed the short URL in MANAGED-CONFIG so short-code remaps propagate without client reconfiguration.

  • Rule groups expansion (v2.9+) — 15 extended built-in rule groups, subscribable Custom RuleSets backed by a provider dictionary (MetaCubeX / blackmatrix7 / Loyalsoldier / ACL4SSR / custom URL) that each register their own proxy group, Custom Rules outbound as a dropdown of valid targets, configurable Fall Back default, and customRuleSets + fallback_outbound share-URL params.

  • Per-rule no-resolve toggle for Custom Rules IP CIDR (v2.9.2+) — Upstream hard-codes no-resolve on every user-defined IP-CIDR rule, which silently bypasses the rule whenever the client hands the rule engine a hostname instead of an IP (common with Surge's system proxy / HTTPS CONNECT). A switch next to the IP CIDR field drops that flag per rule so the client resolves DNS and the rule actually matches. Default off preserves upstream behavior. Affects Clash / mihomo / Surge output.

  • Custom proxy groups (v2.11+) — a new form section to define your own named policy groups: pick a type (select / url-test / fallback / load-balance) and members by referencing existing groups (Node Select = all nodes, Auto Select, region/rule/ruleset groups, Surge devices, other custom groups) plus DIRECT/REJECT. Resolved server-side and emitted across Clash / mihomo / sing-box / Surge with per-platform type degradation, and usable as outbound targets in Custom Rules, Custom RuleSets and the Fall Back selector. New customProxyGroups share-URL param with full short-code round-trip.

See the Changelog below for detailed release notes and migration guidance for each change.

⚠️ Data Retention Notice

Short links and stored configs persist your data, including credentials, to the backend KV store. Before using this tool — especially a public-facing instance — understand exactly what gets written:

/shorten-v2 (and the web UI's "Shorten" button)

Submitting a subscription for shortening writes the full query string you submitted to KV under the generated short code. That query string includes the config parameter, which carries:

  • Raw proxy URIs (vmess://, vless://, ss://, hysteria2://, trojan://, tuic://, …) — these encode server addresses, UUIDs, passwords, and pre-shared keys.
  • HTTP/HTTPS subscription URLs you pasted in.
  • Your selected rules and custom rule sets.

Storage format (v2.5+): { "q": "<full query string>", "t": "<32-hex token>" }. Legacy entries (pre-v2.5) store the raw query string only.

POST /config (custom base config upload)

Uploading a custom Sing-Box / Clash / Surge base config writes the entire config body to KV under a generated ID. If your base config contains account credentials, policy rules, or DNS settings you consider private, they are persisted as-is.

Where "the backend KV" actually is

  • Self-hosted (Cloudflare Workers / Vercel KV / Upstash Redis / local Redis via Docker): data lives in the KV backend you configured. Retention, access, and deletion are your responsibility.
  • Public instance: data lives in the KV of whoever operates that instance. Treat any public instance as you would any untrusted third party that now holds your proxy credentials.

TTL

Short links and stored configs respect the optional TTL configured via shortLinkTtlSeconds / configTtlSeconds. If unset, entries persist indefinitely until manually deleted.

Recommendation

If your subscription contains credentials you consider sensitive, self-host rather than using a public instance, and configure a reasonable TTL.

🚀 Quick Start

One-Click Deployment

  • Choose a "deploy" button above to click
  • That's it! See the Document for more information.

Alternative Runtimes

  • Node.js: npm run build:node && node dist/node-server.cjs
  • Vercel: vercel deploy (configure KV in project settings)
  • Docker: docker pull callmewaynewu/sublink-worker:latest
  • Docker Compose: docker compose up -d (includes Redis)

✨ Features

Supported Protocols

ShadowSocks • VMess • VLESS • Hysteria2 • Trojan • TUIC • Snell¹

¹ Snell outputs natively to Surge and Clash (Mihomo). Sing-Box has no native Snell outbound — Snell nodes are dropped with a console warning.

Client Support

Sing-Box • Clash • Xray/V2Ray • Surge

Input Support

  • Base64 subscriptions
  • HTTP/HTTPS subscriptions
  • Full configs (Sing-Box JSON, Clash YAML, Surge INI)
  • snell:// share-links (tool-internal format — see Snell below)

Snell

  • Input:
    • Surge config block (paste full text containing [Proxy] section)
    • Clash YAML (type: snell node, supports obfs-opts: {mode, host})
    • snell:// share-link — tool-internal format, not a community standard:
      snell://<url-encoded-psk>@<host>:<port>?version=<n>&obfs=<http|tls>&obfs-host=<h>&tfo=<bool>&reuse=<bool>&udp=<bool>#<name>
      
      Only psk and host:port are required. snell:// URLs from other tools (Surgio, etc.) use different conventions and are not guaranteed to parse.
  • Output: Surge ✓ native · Clash (Mihomo) ✓ native · Sing-Box ✗ dropped with a console warning.

Core Capabilities

  • Import subscriptions from multiple sources
  • Generate fixed/random short links (KV-based)
  • Light/Dark theme toggle
  • Flexible API for script automation
  • Multi-language support (Chinese, English, Persian, Russian)
  • Web interface with predefined rule sets and customizable policy groups

🧩 Rule Groups

Extended rule groups

15 additional rule groups (Discord, WhatsApp, Signal, Line, Zoom, Spotify, News, Reddit, Twitch, Pixiv, Developer, OpenAI, Anthropic, Speedtest, Porn) are available behind a "Show more rule groups" disclosure in the Rule Selection card. They are not included in any preset; check them individually when you want them. Presets (minimal, balanced, comprehensive) are unchanged.

Custom RuleSets

Subscribe to any public rule-set file and register it as an independent proxy group. Expand Custom RuleSets under Advanced Options and add an entry:

  • Provider: pick from MetaCubeX, blackmatrix7, Loyalsoldier, ACL4SSR, or Custom URL
  • File name (non-Custom URL providers): file stem such as reddit, spotify, Notion — URL is derived automatically for sing-box/Clash/Surge
  • Custom URL: supply one URL per format (sing-box .srs, Clash .mrs/.yaml, Surge .list). Only formats you fill in will emit
  • Type: site for domain rule-sets, ip for IP CIDR rule-sets
  • Outbound: proxy group for matched traffic (Proxy, Direct, Reject, or any selector name)

Entries round-trip through share links via the customRuleSets URL parameter. Providers that do not publish a given format (e.g. ACL4SSR has no sing-box .srs) are silently skipped on export — mixed-format subscriptions stay valid.

Fall Back outbound

The Fall Back selector's default member (what unmatched traffic uses until the user switches) is now configurable from Advanced Options → General Settings. Choose Node Select (default), DIRECT, or REJECT.

🗒️ Changelog

v2.11.2

  • Fix: a custom proxy group that shares a name with a custom rule / rule set is no longer shadowed. When a rule routed to a same-named group (e.g. a Surge DEVICE: group reached via DOMAIN-SUFFIX,…,Ponte MacMini), the auto-generated rule-group claimed the name first and the user's group was dropped as a duplicate — emitting the full node list instead of its declared members. The explicit group now owns the name across Clash / sing-box / Surge; the member resolver is platform-aware, so a device-only group survives on Surge and is correctly empty on Clash/sing-box (where the rule falls back to a node selector).
  • Change: custom proxy groups are no longer auto-listed as members of the other policy groups. They previously appeared in Node Select and every rule group's option list. They now stand alone — still usable as routing targets in Custom Rules / Custom RuleSets, and, when explicitly chosen, as the Fall Back / rule-set default (where a select group must list its default).

v2.11.1

  • Fix: custom proxy groups now drop member references whose target was removed. A member pointing at a selected rule / custom rule set / Surge device / another custom group lingered as a stale chip after that target was deselected, renamed, or deleted. Output was already correct (the server drops unknown references at build time); this fixes the lingering UI state. The form now prunes invalid members when those sections change, matching Custom Rules / Custom RuleSets.

v2.11.0

  • Custom proxy groups (自定义策略组). New form section to define named policy groups with a chosen type (select / url-test / fallback / load-balance) and members selected from existing groups — Node Select (= all nodes), Auto Select, region / rule / rule-set groups, Surge (Ponte) devices, other custom groups — plus DIRECT/REJECT. Members are references only; "Node Select" covers "all nodes" so there is no node-name regex and no per-group test-URL config (parity with the built-in Auto Select). Groups are validated and emitted server-side and stay consistent across Clash / mihomo / sing-box / Surge: types with no native equivalent degrade to the closest auto type (sing-box fallback/load-balanceurltest; Surge load-balanceurl-test). Custom groups are first-class outbound targets in Custom Rules, Custom RuleSets and the Fall Back selector. DEVICE: members are kept on Surge and dropped on Clash/sing-box (they have no Ponte). New customProxyGroups share-URL parameter, including short-code / "Load from Code" restore.

v2.10.4

  • Fix: Surge devices were dropped from generated subscription URLs and short codes. submitForm() (the "Generate" button) built its query string independently from the share-link path and forgot to read the surgeDevices hidden input. Long /surge URLs that the user copied — and any short codes those URLs were folded into via /shorten-v2 — silently lost the declared device list, so reloading from a short code restored Custom Rules and Custom Rule Sets but never the Surge Devices section. Without those declarations, the next validateOutbounds() pass auto-reset every DEVICE:<name> outbound to Node Select.
  • UI: align Custom Rule Sets empty state with the other two sections. The "no rule sets yet" placeholder was missing the round icon container that Custom Rules and Surge Devices use, which made the empty state look unfinished.

v2.10.3

  • Preserve [Host], [URL Rewrite], [Header Rewrite], [MITM], [Script], [SSID Setting] from a custom Surge base config. The base-config validator previously rejected any Surge INI that did not contain [General] / [Replica] / [Proxy] / [Proxy Group], so a valid fragment containing only [Host] *.company.ponte = 127.0.0.1 failed validation. Even when accepted, those sections were silently dropped from the emitted config because the builder only ever produced the five generated sections. Now the parser recognizes the six common passthrough sections, stores each as a raw-line array, and the validator accepts any input with at least one recognized section (a [Rule]-only fragment also parses now). On emission, every non-empty passthrough section is appended verbatim in canonical Surge order after [Rule]. Lines round-trip exactly as written; comments and blank lines are stripped (existing INI parser behavior). The JSON-format Surge base config path now applies the same recognized-section check, so {} / null / arrays / objects with only unrelated keys are rejected up-front instead of silently passing validation and breaking later.
  • Fix saved configs disappearing under the in-memory KV. MemoryKVAdapter.scheduleExpiration called setTimeout(fn, ttlMs) directly, which Node.js silently downgrades to 1 ms when ttlMs >= 2^31 (~24.85 days). The default configTtlSeconds is 30 days, so every config saved via POST /config was deleted on the next event-loop tick — the subsequent /surge?configId=… request found null and silently fell back to the default base config. Switched to recording an absolute expiry timestamp at put time and checking lazily on get. Anyone running the node-server / Vercel build without Redis was affected; Cloudflare Workers / Upstash / Redis-backed deployments were not.
  • UI animation parity across Surge Devices, Custom Rule Sets, and Custom Rules. All three sections now share the same enter/leave animation for adding, removing, and clearing entries, and the same fade-scale transition between Form and JSON modes. Surge Devices previously had no animation at all; Custom Rule Sets had per-card animation but no mode-switch fade.

v2.10.2

  • Surge DEVICE:device_name outbound for custom rules and rule sets. A new "Surge Devices" section lets you declare Ponte device names (e.g. tower, my-iphone); declared names then appear as DEVICE:<name> options in the Custom Rules and Custom Rule Sets outbound dropdowns. In the Surge config, the DEVICE: policy is emitted verbatim — no wrapper proxy group — so traffic routes directly to the named device (DOMAIN-SUFFIX,work.com,DEVICE:my-iphone and RULE-SET,<url>,DEVICE:tower). Clash and Sing-Box have no equivalent; rules and rule sets with a DEVICE: outbound are silently dropped from those configs (no wrapper group, no rule-provider, no orphan rule-set declaration). The device list is persisted in the subconverter URL alongside customRules and customRuleSets, restored before either of those so cross-references survive a round-trip.

v2.10.1

Sync six bug fixes from upstream 7Sageer/sublink-worker. None of these affect the existing Snell support added in v2.10.0.

  • Restore full proxy choices in Custom Rule selectors (#371). Custom Rule groups (e.g. "YouTube → MyProxy") now include individual nodes alongside the existing Node Select / Auto Select / Manual Switch / DIRECT chain — previously the node list was missing, so users could only route through aggregate selectors.
  • Preserve subscription-userinfo on /xray (#362, #382). The xray endpoint now passes the upstream subscription-userinfo HTTP header through to the client, matching the existing behaviour of /singbox / /clash / /surge. Surge / Stash / Loon clients in xray mode now show traffic and expiry information again.
  • Better remote subscription decoding. decodeContent no longer base64-decodes payloads that are already plain Surge / Clash / Sing-Box config or ss:///vmess:///etc. share-link lists, avoiding garbled output. Surge config text is now recognized in detectFormat.
  • Reject empty Clash proxy groups (#378). User-supplied url-test / fallback groups with empty proxies: [] and no use: references now produce a 400 error with the offending group name, instead of being silently filled with all available nodes.
  • Sing-Box 1.11+ schema (#380). Removed the legacy {type:'block', tag:'REJECT'} special outbound and the deprecated independent_cache field from the base Sing-Box config. Ad Block rules now emit action: reject route actions (1.11+ idiom) rather than routing to a REJECT outbound. Selector groups in sing-box no longer include REJECT. A new sanitization pass strips legacy block / dns outbound references from user-uploaded base configs.
  • Stable auto-provider names (#379). Provider auto-tags are now _auto_provider_<base36 hash> derived deterministically from the source URL (FNV-1a 32-bit), instead of _auto_provider_1 / _2 indexes that shifted on every build. Duplicate URLs are deduplicated; hash collisions get _2 / _3 suffixes. Affects both Clash proxy-providers and Sing-Box outbound_providers. Cached providers will be re-downloaded once after the upgrade.

v2.10.0

  • Snell protocol support. Parses Snell nodes from Surge config blocks, Clash YAML (type: snell with obfs-opts), and a new tool-internal snell:// share-link form (snell://<psk>@<host>:<port>?version=&obfs=&obfs-host=&tfo=&reuse=&udp=#name). Outputs natively to Surge and Clash (Mihomo); Sing-Box has no native Snell outbound, so Snell nodes are dropped with a console warning and excluded from selector groups. The snell:// URL is not a community standard — URLs from other tools (Surgio, etc.) follow different conventions and are not guaranteed to round-trip.

v2.9.2

  • Per-rule no-resolve toggle for Custom Rules IP CIDR. Upstream emits every user-defined IP-CIDR rule with no-resolve hard-coded, so the rule silently never matches when the client evaluates rules against a hostname instead of an IP (common with Surge's system proxy / HTTPS CONNECT — traffic falls through to Final). This fork adds a switch next to each custom rule's IP CIDR field that drops the no-resolve flag when enabled, letting the client resolve DNS so the IP rule can actually match. Default off preserves upstream behavior. Affects Clash / mihomo / Surge output; sing-box has no equivalent flag and is unchanged.

v2.9.1

  • Bug fix: custom rules / rule sets named after a reserved outbound (e.g. DIRECT, REJECT, PASS) no longer generate a same-named selector group. Surge previously rejected the config with "策略组不可以使用内部策略名"; other clients silently shadowed the built-in action.
  • Dropped the redundant DIRECT = direct line from Surge's [Proxy] section.

v2.9.0

  • 15 extended rule groups behind a collapsed disclosure (Discord, WhatsApp, Signal, Line, Zoom, Spotify, News, Reddit, Twitch, Pixiv, Developer, OpenAI, Anthropic, Speedtest, Porn). Presets are unchanged.
  • Custom RuleSets — subscribe to public rule-set files (MetaCubeX / blackmatrix7 / Loyalsoldier / ACL4SSR / custom URL). Each entry becomes its own proxy group; the form's Outbound field is that group's default member. Share-link round-trip via customRuleSets.
  • Custom Rules outbound is now a dropdown of valid outbounds (built-in + selected rule groups + custom rule sets) instead of a free-text group name.
  • Fall Back outbound preference (Node Select / DIRECT / REJECT).
  • Global <select> chevron + consistent padding, animated row add/delete, auto-reset of referenced outbounds when the source is removed.

v2.7

Surge #!MANAGED-CONFIG short-URL preservation. Surge responses previously embedded the long converter URL (e.g. /surge?config=...) in their #!MANAGED-CONFIG directive.

  • When a Surge client subscribes via a short link (/s/:code), the returned config's #!MANAGED-CONFIG line now points at the short URL (e.g. https://<host>/s/abc123). The client stays pinned to the short link; subsequent /shorten-v2 overwrites of the same code are automatically picked up on the next client refresh, with no manual reconfiguration.
  • Direct access to /surge?config=... (no short link involved) is unchanged — the long request URL is written into MANAGED-CONFIG.
  • A new optional query parameter sub_url is accepted by /surge. It must be a same-origin absolute URL; cross-origin or malformed values are silently ignored (stripped from the fallback URL) to prevent malicious URL override.

One-time migration for existing subscribers: Surge clients already pinned to the long URL (from an earlier build) will not auto-migrate. Re-enter the short URL in Surge once to pick up the new behavior.

v2.6

Short code loader UI + /resolve read authentication. Building on v2.5's token system.

  • New UI entry point: a "Load from Code" button appears to the left of Paste/Clear on the main input. It opens a modal accepting a short code and optional token, then loads the original subscription configuration back into the form and captures the token so a subsequent "Shorten" call overwrites the same short code.
  • /resolve is now conditionally authenticated: entries created under v2.5+ require the matching X-Shortlink-Token header. Missing token → 401 (reason missing). Wrong token → 403 (reason mismatch). Legacy entries (created before v2.5) remain anonymously readable.
  • Auto-parse of pasted short URLs has been removed. Pasting a short URL (e.g. https://<host>/b/<code>) into the main input textarea no longer fetches and populates the form. Use the new "Load from Code" button instead.
  • /b/:code, /c/:code, /x/:code, /s/:code redirect endpoints are unchanged — they continue to resolve anonymously so existing short links on the open internet keep working.

Migration: any external tooling that called /resolve on a new-format short code must now supply X-Shortlink-Token.

v2.5

Short link token authentication on the /shorten-v2 endpoint.

  • Response is now JSON (previously text/plain). Shape: { "code": "<shortcode>", "token": "<32-hex-token>" }.
  • Overwriting an existing short code requires sending the X-Shortlink-Token: <token> header. The token is returned exactly once, on creation — save it.
  • Legacy short links (created before this version) are tokenless. The first caller who references such a short code will claim it and receive a fresh token; after that, subsequent overwrites require that token.
  • 403 responses (JSON { error, reason } with reason being missing or mismatch) are returned when authorization fails.

Migration: external scripts that read /shorten-v2 response as text must parse JSON and handle the new token field.

🤝 Contributing

Issues and Pull Requests are welcome to improve this project.

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

⚠️ Disclaimer

This project is for learning and exchange purposes only. Please do not use it for illegal purposes. All consequences resulting from the use of this project are solely the responsibility of the user and are not related to the developer.

💰 Sponsorship

Thanks to the following sponsors for their support of this project

YXVM NodeSupport

If you would like to sponsor this project, please contact the developer @7Sageer

⭐ Star History

Thanks to everyone who has starred this project! 🌟

Star History Chart