Scientific Surfing
A CLI for surfing the internet scientifically, written in Go.
Features
- Clash RSS Subscription Support: Download and transform clash rss subscriptions
- Hook System: Customizable scripts for extending functionality
- Core Configuration Management: Import, export, and manage core configurations
- Binary Management: Automatic updates for core (mihomo) components
- Service Management: Install/start/stop/reload mihomo as a system service on Linux (systemd), macOS (launchd), and Windows (native Service Control Manager)
Installation
1. Clone and build
git clone ssh://git@gitea.epss.net.cn:2223/klesh/ss.git
cd ss
make build
This produces a single ssm (or ssm.exe on Windows) binary with no runtime dependencies — templates and hook scripts are embedded in the binary.
2. Add the binary to your system PATH
Quick Start
Subscription Management
# add a subscription
ssm subscription add <name> <clash-rss-subscription-url>
# refresh a subscription (with optional backup)
ssm subscription refresh <name> [--backup]
# delete a subscription
ssm subscription rm <name>
# rename a subscription
ssm subscription rename <name> <new-name>
# update subscription URL
ssm subscription set-url <name> <new-url>
# show the URL for a subscription
ssm subscription get-url <name>
# activate a subscription (additive — other active subscriptions stay active)
ssm subscription activate <name>
# deactivate a subscription
ssm subscription deactivate <name>
# move a subscription before/after another (affects list display order and
# config apply's merge order — e.g. proxy/proxy-group ordering)
ssm subscription move <name> --before <target>
ssm subscription move <name> --after <target>
# list all subscriptions
ssm subscription list
# show storage information
ssm subscription storage
More than one subscription can be active at the same time — config apply merges the proxies (and any proxy-groups a subscription bundles, like an "Auto"/"Proxy" group) from every active subscription into the generated config, prefixing each proxy and proxy-group name with its source subscription's name (e.g. home-sub | HK 01) so identically-named ones from different subscriptions don't collide. Any reference to a renamed proxy/group within that subscription's own groups or rules (e.g. DOMAIN,example.com,Proxy, MATCH,Auto) is rewritten to match, so nothing ends up pointing at a name that no longer exists.
Hook Management
# initialize hooks directory with template scripts
ssm hook init
# show hooks directory location and list all scripts
ssm hook list
# edit a hook script with system editor
ssm hook edit <script-name>
# remove a hook script
ssm hook rm <script-name>
Core Configuration Management
# import configuration from file
ssm config import <file-path> [--config-file <config.yaml>]
# export configuration to file
ssm config export <file-path> [--config-file <config.yaml>]
# edit configuration with system editor
ssm config edit [--config-file <config.yaml>]
# reset configuration to default values
ssm config reset [--config-file <config.yaml>]
# show current configuration
ssm config show [--config-file <config.yaml>]
# apply subscription to generate final config (with advanced options)
ssm config apply \
[--config-file <config.yaml>] \
[--output-file <output.yaml>] \
[--subscription <subscription-name>]
Options (available on every ssm config subcommand):
--config-file <config.yaml>: Use a custom config file instead of the default.--output-file <output.yaml>: Specify the output path for the generated config file.--subscription <subscription-name>: Use only this one specific subscription for config generation, overriding the active set entirely (even if multiple subscriptions are active).
Automation (ssm: block in core-config.yaml)
core-config.yaml can carry an ssm: key — this tool's own automation config, applied when you run config apply and always stripped out of the generated config before it's written (mihomo never sees it). It supports three things:
proxy-groups— build a new proxy-group from proxies matching a subscription and/or name-pattern (regexp) filter.filters— strip unwanted proxies out of every proxy-group'sproxieslist in the generated config (subscription-provided groups and ones built by aproxy-groupsrule above) — a global denylist, not a new group.patches— append/prepend/replace an arbitrary value at any path in the generated config.
ssm:
proxy-groups:
- name: "HK Nodes"
type: select # any extra clash proxy-group fields (url, interval, tolerance...) pass through as-is
match:
subscriptions: ["home-sub"] # optional; omit to match proxies from any subscription
name-pattern: '(?i)HK|Hong Kong' # optional; regexp matched against the original (pre-prefix) name of either a proxy OR a proxy-group from the subscription (e.g. its own default selector); use "|" for alternatives and "(?i)" for case-insensitive matching. Never matches a builtin like DIRECT/REJECT — those aren't sourced from any subscription
filters:
- name: "Drop Blocked Nodes" # optional; only used for logging
match:
name-pattern: '(?i)blocked|expired' # proxies matching Match with no `compares` are unwanted outright — a plain denylist by name
- name: "Drop Expensive and Slow"
match:
subscriptions: ["home-sub", "work-sub"] # optional; scopes which proxies this rule even considers
name-pattern: '^(HK|SG)'
compares: # optional (list); every entry must be satisfied (AND'd) for a scoped proxy to be considered unwanted and removed
- pattern: '([\d.]+)x' # regexp whose first capture group is parsed as a float; proxies whose name doesn't match are left alone (can't be evaluated)
operator: ">" # one of <, <=, >, >=, ==, !=
value: 2.0
- pattern: '(\d+)ms'
operator: ">"
value: 300
patches:
- path: dns.nameserver # dot/bracket path: dots descend into maps, [N] indexes into lists
op: prepend # append | prepend | replace
value: ["1.1.1.1"]
- path: proxy-groups[0].proxies
op: append
value: ["DIRECT"]
- path: rules
op: replace
value: ["MATCH,PROXY"]
proxy-groups build new groups first, then filters scrub unwanted proxies out of every group present at that point (subscription-provided and newly built alike), then patches run last — so patches can reference or further adjust the (already-filtered) groups (e.g. proxy-groups[-1]-style indexing isn't supported — use the group's actual index, or patch proxy-groups itself with append/prepend).
Core Management
# update mihomo core binary
ssm core update [--version <version>] [--force]
Service Management
ssm service install [--name <name>] [--description <description>]
ssm service uninstall [--name <name>]
ssm service start [--name <name>]
ssm service stop [--name <name>]
ssm service restart [--name <name>]
ssm service reload [--name <name>] # reload config via mihomo's API, no restart
ssm service status [--name <name>]
install/uninstall/start/stop/restart need root/admin privileges:
- Linux / macOS: just run the command directly —
ssmre-execs itself undersudoautomatically, prompting for your password if needed. - Windows: run from an elevated/Administrator shell —
ssm.exe service install.
Development
make build # build ssm for the current platform
make test # go test ./...
make vet # go vet ./...
make fmt # check formatting (gofmt -l)
make help # list all targets
Releasing
make release VERSION=v1.0.0
Cross-compiles for linux/amd64, darwin/arm64, and windows/amd64, and packages each into dist/ as a .tar.gz/.zip alongside a checksums.txt. Pushing a vX.Y.Z tag runs the same thing via .drone.yml and publishes the artifacts as a Gitea release.
License
MIT License - see LICENSE file for details.