Rules
Rule Types

Rule Types

By writing rules, you can specify the outbound handling for different connections, such as forwarding or intercepting through a specific proxy. Rules can match based on the IP, domain name, process name, or a combination of multiple conditions.

For each connection, the system always matches rules from top to bottom.

Rules can be divided into the following types, among which the IP type may trigger DNS resolution:

  • Based on Domain Name
  • Based on IP
  • Other Composite Types
💡

Want to write rules for URLs? Please read the HTTP Rewrite chapter.

💡

You can add the no-track parameter at the end of a rule to hide connections that match this rule, such as SCRIPT,quic,REJECT,no-track. This is effective for avoiding a large number of REJECT records cluttering the page.

💡

You can use the built-in proxies REJECT and REJECT-DROP to intercept connections. REJECT will immediately return an error, while REJECT-DROP will silently discard the connection to avoid generating connection storms.

DOMAIN

Exact match of domain names, e.g., DOMAIN,google.com matches google.com but does not match www.google.com.

DOMAIN-SUFFIX

Matches domain suffixes, e.g., DOMAIN-SUFFIX,google.com matches both google.com and www.google.com.

DOMAIN-KEYWORD

Keyword matches domain names, e.g., DOMAIN-KEYWORD,google matches both google.com and google.jp.

GEOIP

Matches country codes through MaxMind GeoIP, e.g., GEOIP,CN. You can add no-resolve to avoid triggering DNS resolution.

💡

Stash allows users to replace databases that are compliant with MaxMind GeoIP format. Users can choose a MaxMind GeoIP database that better fits their requirements.

IP-ASN

Matches based on IP Autonomous System Numbers, e.g., IP-ASN,714. You can add no-resolve to avoid triggering DNS resolution.

IP-CIDR / IP-CIDR6

IP CIDR range matching, e.g., IP-CIDR,192.168.1.0/24. You can add no-resolve to avoid triggering DNS resolution.

DST-PORT

Matches target ports, e.g., DST-PORT,80.

RULE-SET

When referencing a large number of rules, please use Rule Sets.

GEOSITE

domain-list-community (opens in a new tab) is a domain list maintained by the v2fly community.

For example, GEOSITE,twitter matches domains related to Twitter (opens in a new tab):

ads-twitter.com
cms-twdigitalassets.com
periscope.tv
pscp.tv
t.co
tellapart.com
tweetdeck.com
twimg.com
twitpic.com
twitter.biz
twitter.com
twitter.jp
twittercommunity.com
twitterflightschool.com
twitterinc.com
twitteroauth.com
twitterstat.us
twtrdns.net
twttr.com
twttr.net
twvid.com
vine.co
x.com
⚠️

The domain-list-community data is not distributed with Stash; Stash will load domain data from github.com on demand during the first use. Please ensure that your current configuration has connectivity to github.com during the first usage.

PROCESS-NAME

Matches process names, e.g., PROCESS-NAME,Telegram. Only valid for local processes.

⚠️

Due to limitations with the Network Extension, Stash iOS/tvOS (including the iOS version running on Apple silicon devices) does not support PROCESS-NAME rules, and any process-related rules in the configuration will be ignored.

PROCESS-PATH

Matches process paths, e.g., PROCESS-PATH,/Applications/Telegram.app/Contents/MacOS/Telegram. Only valid for local processes.

⚠️

Due to limitations with the Network Extension, Stash iOS/tvOS (including the iOS version running on Apple silicon devices) does not support PROCESS-PATH rules, and any process-related rules in the configuration will be ignored.

SCRIPT

Matches requests using Python expressions. The expression must return a Boolean value, and expressions that result in errors will be ignored.

Expressions can read the following variables:

{
  "network": "string", // could be tcp or udp
  "host": "string", // may be empty
  "dst_ip": "string", // may be empty
  "dst_port": "number",
  "src_ip": "string", // only valid in gateway mode
  "src_port": "number" // only valid in gateway mode
}

Expressions can call the following functions:

def resolve_ip(host: str) -> str:
    pass
 
def in_cidr(ip: str, cidr: str) -> bool:
    pass
 
def geoip(ip: str) -> str:
    pass
 
def ipasn(ip: str) -> int:
    pass
 
def match_provider(name: str) -> bool:
    pass
 
def match_geosite(name: str) -> bool:
    pass

For example, to intercept QUIC protocol requests, you can write:

rules:
  - SCRIPT,quic,REJECT
  - SCRIPT,udp-cn,ProxyToCN
 
script:
  shortcuts: # can be referenced in rule
    quic: network == 'udp' and dst_port == 443 # matches QUIC protocol
    udp-cn: network == 'udp' and geoip(dst_ip if dst_ip != '' else resolve_ip(host)) == 'CN' # matches UDP going to CN
    instagram-quic: network == 'udp' and dst_port == 443 and match_geosite('instagram') # matches Instagram's QUIC