Module
Chute supports external module files (.sgmodule) to extend configuration with reusable, shareable rules, scripts, MitM hosts, and DNS mappings. Modules follow the Surge module format.
Modules are defined in the [Module] section:
[Module]
https://example.com/my-module.sgmodule
/Users/me/.chute/custom-module.sgmodule
~/.chute/another-module.sgmodule
http:// and https:// modules are fetched remotely; a local file is written as a bare path — /path/to/local-module.sgmodule or ~/.chute/local-module.sgmodule — which is also the form the configuration is saved back in. A file:/// URL works as well. A local module file that cannot be read is skipped with a warning in the log.
Module File Structure
A .sgmodule file follows the same syntax as the main configuration file. Supported sections:
| Section | Purpose |
|---|---|
[MITM] |
Add hostnames to the MitM host list |
[Script] |
Register JavaScript scripts |
[URL Rewrite] |
Add URL rewrite rules |
[Header Rewrite] |
Add header rewrite rules |
[Rule] |
Add routing rules |
[Host] |
Add DNS host-to-IP mappings |
[Map Local] |
Add Mock Response rules |
[Body Rewrite] |
Add body rewrite rules |
[General] |
Extend a few list options (see below) |
For compatibility, a
[DNS]section in a module is accepted as an alias of[Host].
Module Metadata
Modules can include metadata directives (lines starting with #!):
#!name = My Custom Module
#!desc = Blocks ads and trackers for example.com
#!system = ios,macos
| Directive | Description |
|---|---|
#!name |
Module name (displayed in UI) |
#!desc |
Module description |
#!system |
Platform filter: ios, macos (comma-separated) |
#!arguments |
Declares module variables with default values (e.g. #!arguments = var1:default1, var2:default2) |
#!system_version |
Surge's minimum system version; kept as information, not enforced |
#!REQUIREMENT |
Surge's version requirement (also written //!REQUIREMENT); recorded and ignored |
#!system is informational metadata shown in the module editor; it does not currently gate loading — the module is applied on all platforms.
#!arguments may appear multiple times; later declarations override earlier ones. {{{variable}}} placeholders in the module body are replaced with the variable's #!arguments default, or with the empty string when it declares none. No app currently offers a way to override a module's arguments, so the declared defaults are always what applies.
A line prefixed with #!IOS-ONLY, #!MACOS-ONLY or #!TVOS-ONLY applies only on that platform and is dropped elsewhere, wherever it appears — in a module, and in the configuration file itself:
[Rule]
#!IOS-ONLY DOMAIN-SUFFIX,mobile-ads.example,REJECT
#!MACOS-ONLY PROCESS-NAME,Updater,REJECT
When the app saves the configuration the prefix is kept as written: a line excluded on this platform is neither dropped from the file nor turned into a comment, so the same profile keeps working on the other platforms.
Module Example
#!name = Ad Block Module
#!desc = Block common ad domains
#!system = ios,macos
[Rule]
DOMAIN-SUFFIX,doubleclick.net,REJECT
DOMAIN-SUFFIX,googlesyndication.com,REJECT
DOMAIN-SUFFIX,googleadservices.com,REJECT
[Host]
localhost = 127.0.0.1
[MITM]
hostname = *.google-analytics.com
[URL Rewrite]
^https://example\.com/old-api https://example.com/new-api 302
%APPEND%/%INSERT%directives inside a module'shostnamevalue are stripped and the hostnames merged into the MitM list; a leading-exclusion prefix on a hostname is preserved.
A module's [General] section can extend five list options of the running configuration — skip-proxy, tun-excluded-routes, tun-included-routes, dns-server and always-real-ip — with %APPEND% (add at the end) or %INSERT% (add at the front). The merge happens for all five; which platforms' tunnels read the two route lists is described under Included Routes and Excluded Routes. The values are merged when the module is applied and withdrawn when it is removed or disabled. A value without a directive is appended as well, with a notice, because replacing the whole list is not supported; any other [General] key is ignored with a notice.
[General]
skip-proxy = %APPEND% *.corp.example, 10.20.0.0/16
Module Lifecycle
- Modules are loaded after the main configuration is parsed.
- Rules, scripts, MitM hosts, and DNS hosts from modules are added to the runtime managers.
- Module
[Rule]lines run after the automatic Tailscale rules and before the configuration's own rules, in module load order — as in Surge, so an ad-blocking module's REJECT is not shadowed by an earlier rule in the profile. [Map Local],[Body Rewrite]and[General]merges from modules are applied again after a configuration reload.- When a module is removed or disabled, all its rules, scripts, and hosts are unregistered.
- Remote module content is fetched when the tunnel starts, and re-fetched only when the
[Module]URL list changes on a configuration reload.