Skip to content

Modules and Local Rules

Modules bundle hostnames and rules into one file that you can install and keep up to date. MeowX reads two formats:

  • Surge modules (.sgmodule), the format Shadowrocket also uses.
  • Loon plugins (.plugin), read by a separate parser because Loon orders fields differently.

MeowX doesn’t include or recommend any modules. Install only modules from sources you trust.

Open Modules from the HTTPS Decryption page (iOS) or the HTTPS Decryption window (Mac), then choose one of:

Method Notes
Import from URL Remote modules are only accepted from https:// addresses. They are checked for updates later.
Paste Imports pasted module text. Not updated automatically.
File Imports a file (for example from the Files app). Not updated automatically.
meowx://install-module?url=… A link that opens MeowX and starts installing the module at the given https:// address (URL-encode the address). On the Mac it opens the Modules page of the HTTPS Decryption window.

The module text must be UTF-8 and at most 4 MB. MeowX decides the format from the file name or URL (.plugin and .lpx are Loon, .sgmodule and .module are Surge) and otherwise from the content: a [Rewrite], [MitM] or [Argument] section or a #!loon_version line means Loon; anything else is read as Surge.

Every installation, including one started from a link, first shows a confirmation page. Nothing is installed unless you tap Install; Don’t Install discards it. The page shows:

  • The name, description, source and author (or “Unknown (not stated in the module)”).
  • Hosts to decrypt: the number of hostnames that will actually be decrypted.
  • Sensitive hosts: hosts that look like banking, payment, government, sign-in (SSO) or password-manager services, grouped by category. If there are any, you must tick “I know these sensitive hosts will be decrypted, and I trust this module’s source” before you can install.
  • Arguments you can fill in now (see Arguments).
  • Rules by type: how many rules of each kind are effective, unsupported or ignored.
  • Routing rules from the [Rule] section, by policy (direct · reject · proxy). Direct rules get a warning because matching traffic skips the proxy, and rules broad enough to catch most traffic are listed individually.
  • A warning when the module contains scripts.
  • Notes such as “uses override syntax” or “skip-server-cert-verify ignored”.
  • What future updates will ask you about.

You can open the full text before installing. Only one installation can wait for confirmation at a time. After you confirm, the module is added at the bottom of the list, enabled, and its rules are compiled.

Each module shows its name, description, switch, when it was imported or last updated, and badges: Update pending Local Effective N Unsupported M Ignored K, or an overall state such as “Whole module unsupported”, “Not for this platform” or “Too many rules, not enabled”.

Higher in the list means higher priority. When several modules change the same request, the one higher up wins:

  • URL rewrites and Map Local use the first matching rule, in list order.
  • Header rewrites, body rewrites and scripts all run, in list order.
  • Hostname %INSERT% and %APPEND% entries and [Rule] routing rules also follow list order.

This is the opposite of Surge’s convention, where tutorials put base modules at the bottom so that later modules override them. A base module that a Surge tutorial puts at the bottom belongs at the top in MeowX.

To reorder: on iOS tap Sort above the list and drag the handles, then tap Done (the long-press menu also has Move Up and Move Down). On the Mac, drag modules in the list, or right-click and choose Move Up / Move Down.

Your own local rules always come before every module.

The details page shows three figures (Effective rules, Unsupported rules and Undecrypted hosts), the compile result, notes, the argument editor, and the actions Check for Updates, View Source and Delete Module. Deleting a module removes its hostnames, rewrites and routing rules.

View Source shows the original text with line numbers. Lines that were ignored, rejected or are unsupported are highlighted in red. The compile result lists each such line with its line number, section and reason, using these labels:

Label Meaning
Effective The rule is active.
Routing A [Rule] routing rule. It follows the module’s switch, not the HTTPS Decryption switch.
Unsupported MeowX doesn’t support this line (yet), for example a cron script or an unknown rule type.
Ignored Understood but deliberately not applied, for example skip-server-cert-verify (security reasons) or a [MITM] key MeowX doesn’t use.
Rejected Not allowed, for example a header rewrite that touches Content-Length or Transfer-Encoding, which would break message framing.
Data not downloaded The rule needs remote data (Map Local file, jq file or script) that hasn’t been downloaded yet.

It also reports wildcard rules: rules whose host can’t be determined from their pattern, so they have to be checked against every request. Many of them slow down matching.

Line Use
#!name= Display name. Without it, the file name is used.
#!desc= Description.
#!author=, #!icon=, #!category=, #!homepage= Shown on the confirmation page or kept for reference.
#!system= Platforms the module is for. Surge uses ios / mac; Loon uses a list such as iOS,macOS. If the current platform isn’t listed, the module isn’t enabled (“Not for this platform”). Without the line, the module works everywhere.
#!requirement= Recorded but not evaluated.
#!arguments=, #!arguments-desc= Surge arguments (below).
#!loon_version= Loon; recorded only.

Arguments let users customize a module without editing it. MeowX substitutes them before parsing.

Surge: declare #!arguments=name:default,name2:"a,b" (quotes allow commas in a default) and write {{{name}}} in the body:

#!name=Example
#!arguments=HOST:api.example.com,MODE:strict
#!arguments-desc=HOST: the API host\nMODE: strict or loose
[MITM]
hostname = %APPEND% {{{HOST}}}
[URL Rewrite]
^https:\/\/{{{HOST}}}\/ads reject

Loon: declare arguments in an [Argument] section as name = input|select|switch,"default",…,tag=Title,desc=Description. For select, every listed value is an option and the first is the default; switch defaults to false. In the body, {{{name}}}, ${name} and {name} are all replaced.

[Argument]
blockSplash = switch,"true",tag=Block splash ads
region = select,"us","jp","hk",tag=Region

Rules:

  • Argument names may contain letters (including non-ASCII letters), digits and underscores, and are case-sensitive.
  • Values you enter in the module’s details override the defaults. Line breaks are removed from values.
  • If the body uses a {{{name}}} that isn’t declared, the whole module is marked unsupported rather than half-parsed. The exception is [Script] and [Body Rewrite], where only that one line is marked unsupported. References in sections MeowX doesn’t use are ignored.
  • Surge’s built-in {{{GATEWAY_ADDRESS}}} isn’t supported; lines using it are marked unsupported.
Section What MeowX does with it
[MITM] hostname and hostname-disabled, with %APPEND% / %INSERT%. See Hostnames.
[URL Rewrite] URL rewrite: reject family, 302, 307, transparent (header).
[Header Rewrite] Header rewrite.
[Map Local] Map Local.
[Body Rewrite] Body rewrite: regex pairs and jq.
[Script] Scripts of type http-request and http-response.
[Rule] Routing rules (below). URL-REGEX rules with a reject policy become URL rewrites.
Anything else ([General], [Proxy], [Host] …) Each line counts as unsupported.
Section What MeowX does with it
[MitM] Same as Surge’s [MITM]. Entries without an operator are appended, as in Loon.
[Rewrite] Classic Loon rewrites (reject family, 302, 307, header, header operations, mock-response-body, body replace and JSON operations) and the newer request if ${url} ~= /…/ then … / response if … syntax. See URL rewrite, Header rewrite, Map Local and Body rewrite.
[Script] http-request and http-response scripts.
[Rule] Routing rules (below).
[Argument] Arguments (above).
Anything else Each line counts as unsupported.
Key Handling
hostname Merged into the hostname list. %INSERT% entries go before your own hostnames, %APPEND% and plain entries after.
hostname-disabled Disables matching positive entries.
skip-server-cert-verify Always ignored for security reasons. Only you can turn this on, in the HTTPS Decryption settings.
ca-p12, ca-passphrase Ignored: modules can’t change the CA.
Other keys (such as h2) Ignored. HTTP/2 is used automatically when both sides support it.

Routing rules decide where connections go. They don’t decrypt anything, so:

  • They follow the module’s own switch and are not affected by the HTTPS Decryption switch.
  • They apply only in Rule mode, after your own custom rules and before all subscription rules, in module list order. The first matching rule wins.
  • Connections they match show the module’s name in the rule column of the connection list.

Supported rule types: DOMAIN, DOMAIN-SUFFIX, DOMAIN-KEYWORD, IP-CIDR, IP-CIDR6 (with no-resolve), and AND / OR / NOT made only of these. Loon’s HOST, HOST-SUFFIX, HOST-KEYWORD and IP6-CIDR are accepted as aliases. The options extended-matching and pre-matching are accepted and have no effect. Other types (RULE-SET, DOMAIN-SET, USER-AGENT, PROCESS-NAME …) are unsupported.

Policy in the module Result
DIRECT Direct
REJECT, REJECT-NO-DROP, REJECT-TINYGIF (and Loon’s REJECT-IMG, REJECT-DICT, REJECT-ARRAY, REJECT-200) Reject
REJECT-DROP Reject by dropping
PROXY (Loon only) Your main proxy group: the group your final MATCH rule points to, otherwise the first selector group. Inactive if the configuration has no proxy groups.
No policy (Loon only) Direct
Anything else Unsupported

URL-REGEX,"pattern",REJECT… rules can’t be decided by connection, so MeowX turns them into URL rewrite rejects that apply to decrypted hosts and plain HTTP. URL-REGEX with DIRECT or PROXY is unsupported.

All modules together can contribute up to 10,000 routing rules (512 KB of rule text). Rules beyond that limit are dropped and a “Too many routing rules” banner appears.

Map Local data (data-type=file with an https:// URL, Loon data-path), jq files (jq-path, Loon jq_file) and scripts (script-path) are downloaded by the app when you install, update or enable a module, cached on the device, and embedded into the compiled rules. The tunnel never downloads anything itself.

  • Only https:// addresses are accepted; relative paths and local file paths are unsupported.
  • Each download times out after 20 seconds.
  • Size limits apply per file and in total (see Limits). Rules whose data couldn’t be downloaded show “Data not downloaded” and stay inactive; a banner offers Retry.

MeowX checks remote modules for updates when you return to the app, at most once every 24 hours (it doesn’t poll in the background). If a check fails, it waits one hour before trying again, doubling each time up to 24 hours. Check for Updates in a module’s details checks immediately.

When a new version arrives, MeowX compares it with the current one (using your argument values):

  • If the changes only remove rules or adjust rules in ways that don’t widen the module’s reach, the new version takes effect directly.
  • Otherwise the module is marked Update pending, the current version stays in use, and you review the changes and choose Adopt Update or Ignore This Update. If you ignore it, MeowX keeps the current version and asks again at a later check.

Changes that need your confirmation:

  • New hostnames (counting only hosts that will actually be decrypted), a new *, a host moved from %APPEND% to %INSERT%, or a large jump in the number of hostnames.
  • Removed exclusions or hostname-disabled entries, or exclusions made ineffective by reordering.
  • New or changed 302 / 307 redirects or transparent rewrites that point to a different host (or whose target can’t be determined), and Map Local data now fetched from a host the old version didn’t use.
  • A new skip-server-cert-verify line (it is still ignored).
  • New or changed Map Local, header, body, redirect or transparent rewrite rules that land on sensitive hosts, and such rules whose hosts can’t be determined.
  • In [Rule]: new or changed direct rules, and broad reject rules (for example NOT rules, IPv4 ranges of /8 or wider, top-level domains).
  • Scripts: any new script, or a script whose address or parameters changed.

Changes that take effect directly: removed rules, narrow reject rules, Loon PROXY rules, and new or changed same-host Map Local, header and body rewrites on non-sensitive hosts that are already decrypted.

Rules written as a literal URL prefix (^https?://host/path) are matched without regular expressions and are cheap; everything else is compiled on demand and cached. Limits keep memory under control:

  • If enabling a module would push the total of literal-prefix rules above 20,000 on iOS or 200,000 on macOS, that module is not enabled (“Too many rules, not enabled”). Use a lighter version of the module or enable fewer modules.
  • If the estimated memory for compiled regular expressions exceeds 2 MB on iOS or 16 MB on macOS, a “Too many rules” banner warns that some rules are compiled when first needed, so the first match is a little slower.

Local Rules is a text editor for your own rules in Surge syntax. Supported sections are [URL Rewrite], [Header Rewrite], [Map Local], [Body Rewrite] and [Script]:

[URL Rewrite]
^https:\/\/api\.example\.com\/v1\/ads reject-dict
[Header Rewrite]
http-request ^https:\/\/api\.example\.com\/ header-replace User-Agent MyDebugClient/1.0
[Map Local]
^https:\/\/api\.example\.com\/v1\/config data-type=text data="{\"debug\":true}" header="Content-Type:application/json"
  • Local rules come before all module rules, so they override modules (Loon’s “local configuration over plugins”).
  • Hostnames don’t belong here: [MITM] and [Rule] lines are reported as unsupported. HTTPS hosts must still be added to the hostname list before they are decrypted; plain http:// requests don’t need it.
  • Arguments aren’t substituted in local rules.
  • Saving validates the text and lists each line that didn’t take effect, with its line number and reason. On the Mac, press Command-S to save.