Skip to content

Rules

In route-mode = "rule", every connection is checked against [rule].list from top to bottom. The first rule that matches decides where the connection goes. The list must end with FINAL.

toml
[rule]
list = [
  "DOMAIN-SUFFIX,corp.example.com,office",
  "PROCESS-NAME,cargo,work",
  "IP-CIDR,10.0.0.0/8,DIRECT,no-resolve",
  "FINAL,DIRECT",
]

Syntax

text
TYPE,payload,target
TYPE,payload,target,no-resolve
FINAL,target
  • TYPE is case-insensitive. MATCH is accepted as another name for FINAL.
  • payload cannot contain a comma.
  • target is DIRECT, REJECT, the name of a [[proxy]], or the name of a [[proxy-group]]. Names are case-sensitive.
  • FINAL must be the last entry, and exactly one FINAL must exist.
  • Rules containing a backslash, such as regular expressions, must use TOML single quotes: 'DOMAIN-REGEX,^ci-[0-9]+\.example\.com$,work'. In double quotes \. is an invalid escape and the whole file fails to load.
  • no-resolve is the only option. On IP rules it means: do not resolve a domain just to test this rule; only match when the destination is already an IP address.

Rule types

Domain

TypeMatchesExample
DOMAINexactly this nameDOMAIN,api.github.com,work
DOMAIN-SUFFIXthis name and all subdomainsDOMAIN-SUFFIX,github.com,work
DOMAIN-KEYWORDnames containing the textDOMAIN-KEYWORD,tracker,REJECT
DOMAIN-WILDCARD* and ? patternsDOMAIN-WILDCARD,*.cdn.example.com,DIRECT
DOMAIN-REGEXa regular expressionDOMAIN-REGEX,^ci-[0-9]+\.example\.com$,work
GEOSITEa category from a GeoSite fileGEOSITE,github,work

IP

TypeMatchesExample
IP-CIDRan IPv4 networkIP-CIDR,10.0.0.0/8,DIRECT,no-resolve
IP-CIDR6an IPv6 networkIP-CIDR6,fd00::/8,DIRECT,no-resolve
GEOIPa country or category from a GeoIP fileGEOIP,private,DIRECT,no-resolve
IP-ASNan autonomous system numberIP-ASN,13335,DIRECT

Connection

TypeMatchesExample
DST-PORTa destination port or range a-bDST-PORT,22,DIRECT
NETWORKTCP or UDPNETWORK,UDP,DIRECT

Process

Process rules need to know which program opened the connection. On Linux that comes from eBPF Enhanced Mode; connections that arrive on the local HTTP/SOCKS5 port carry process information only when the platform can provide it.

TypeMatchesExample
PROCESS-NAMEprocess name; * and ? allowedPROCESS-NAME,cargo,work
PROCESS-PATHabsolute executable path; * and ? allowedPROCESS-PATH,/usr/bin/git,work
PROCESS-NAME-REGEXprocess name, regular expressionPROCESS-NAME-REGEX,^python3(\.[0-9]+)?$,work
PROCESS-PATH-REGEXexecutable path, regular expressionPROCESS-PATH-REGEX,^/opt/tools/,work

On Linux the process name is the kernel comm, at most 15 characters: git-remote-https is seen as git-remote-http. Name matching is case-sensitive. See Rule recipes for developer tools for real examples.

Rule set

TypeMatchesExample
RULE-SETany entry of a named rule setRULE-SET,ads,REJECT

See Rule sets below.

AND, OR, NOT and SUB-RULE are recognized names, but their payload needs commas, which the rule syntax does not allow, so they cannot be used in [rule].list.

Ordering

First match wins, so put narrow rules above broad ones:

toml
[rule]
list = [
  "DOMAIN,status.corp.example.com,DIRECT",   # exception first
  "DOMAIN-SUFFIX,corp.example.com,office",   # then the general case
  "FINAL,DIRECT",
]

A readable layout for larger lists: blocks (REJECT), process names, domains, networks, rule sets, then FINAL.

GeoIP and GeoSite

GEOIP and GEOSITE read V2Ray-format data files (geoip.dat, geosite.dat, as published by v2fly and Loyalsoldier). Tell Specola where they are with [rule.cache]:

toml
[rule]
list = [
  "GEOIP,private,DIRECT,no-resolve",
  "GEOSITE,github,work",
  "FINAL,DIRECT",
]

[rule.cache]
geoip = "/home/me/.local/share/specola/geoip.dat"
geosite = "/home/me/.local/share/specola/geosite.dat"

Without the file, validation fails with GEOIP rule requires a geoip resource. Category names are case-insensitive. Use absolute paths.

IP-ASN needs a text catalog in [rule.cache] asn = "<path>", one AS<number>,<cidr> pair per line.

Rule sets

A rule set is a named list of rules, stored in a file or downloaded, that you use with RULE-SET. Declare it under [rule.cache]:

toml
[rule]
list = [
  "RULE-SET,ads,REJECT",
  "RULE-SET,internal,office",
  "FINAL,DIRECT",
]

[[rule.cache.rule-set]]
name = "ads"
behavior = "domain"
url = "https://lists.example.com/ads.txt"

[[rule.cache.rule-set]]
name = "internal"
behavior = "classical"
path = "/home/me/.config/specola/internal-rules.txt"
FieldNotes
namerequired; used as the RULE-SET payload
pathlocal file; with url, where the download is stored
urldownload location; fetched every time the profile loads
behaviorclassical (default), domain or ipcidr
proxydownload through this [[proxy]]

File formats, one entry per line; empty lines and lines starting with # or // are skipped:

behaviorLine format
classicalTYPE,payload without a target, for example DOMAIN-SUFFIX,example.com
domainexample.com (suffix), or prefixed full:, domain:, keyword:, regexp:
ipcidr10.0.0.0/8 or fd00::/8

The target always comes from the RULE-SET rule that uses the set.

Troubleshooting

MessageCause
Rule must use TYPE,payload,target or FINAL,targetwrong number of commas, often a comma inside the payload
Unsupported rule typemisspelled type
Rule references unknown outbound: XX is not DIRECT, REJECT, a proxy or a group; check capitalization
FINAL must be the last rulemove FINAL to the end
Rule list requires an explicit FINAL targetadd "FINAL,<target>"
RULE-SET references unknown cache: Xno [[rule.cache.rule-set]] with name = "X"

If a domain rule does not match traffic captured by Enhanced Mode, the application probably resolved the name on its own; see DNS.