Profile manual
Groups
On this page
[Proxy Group] holds groups: policies that hand each connection to one
of their members. A rule names a group as it names a proxy.
[Proxy Group]
Proxy = select, Auto, Server 1, JP 01, DIRECT
Auto = url-test, Server 1, Server 2, JP 01, interval=600, tolerance=50
Backup = fallback, Server 1, SG 01, timeout=3
Spread = load-balance, policy-path=https://example.com/nodes.list, persistent=true
Best = smart, include-all-proxies=true, policy-priority="Premium:0.9;Backup:1.3"
Home = subnet, default = Proxy, SSID:Home Wi-Fi = DIRECT, TYPE:CELLULAR = Auto
The line#
Name = type, member, …, key=value, …
- The name is what rules and other groups call the group. A group and a proxy cannot share a name, and a group cannot take the name of a built-in policy: either is an error.
- The type is one of those below, in any case. Any other is an error.
- Members are named in order: proxies, other groups, built-in
policies. A component holding
=is a parameter; every other one names a member. A member name holding a comma is quoted. - Parameters follow as
key=value, in any order among the members.
A member that is not a proxy, a group or a built-in policy is an error. A
proxy that cannot be used is left out of every group (its own line says
why). A group left with no usable member refuses traffic, with a warning.
It falls back to DIRECT only when the group explicitly lists DIRECT.
Types#
| Type | Hands a connection to |
|---|---|
select |
the member picked by hand |
url-test |
the member with the lowest latency |
fallback |
the first member that answers |
load-balance |
one of the members that answer, at random, or by host |
smart |
the member real connections do best on |
subnet |
the member for the network the device is on |
url-test, fallback, load-balance and smart are automatic groups:
they choose by testing their members.
select#
Proxy = select, Server 1, JP 01, US 01, DIRECT
The member picked by hand, in the app. The pick is remembered across
restarts and reloads. default=JP 01 chooses the initial member without
changing the displayed member order. A valid remembered pick takes
precedence; if that pick is gone, the configured default is used. Without
an available default, the first member is used and an unavailable default
is reported.
url-test#
Auto = url-test, Server 1, Server 2, JP 01, tolerance=50
The member whose last test was fastest. It stays with its pick until
another member is faster by more than tolerance milliseconds (100
unless it says), so that it does not swing between members that are
about as fast.
fallback#
Backup = fallback, Server 1, SG 01, US 01
The first member, in order, that answered its last test. When none answered, the pick stays where it was.
load-balance#
Spread = load-balance, Server 1, Server 2, Server 3, persistent=true
One of the members that answered the last test, at random for each
connection. With persistent=true, always the same one for the same host,
so that a site sees one address. When no member answered, any member.
strategy makes the choice explicit: random, round-robin, or
consistent-hashing. Round-robin visits available members in order, one
per connection, and skips members that failed their health checks.
Consistent hashing uses the full destination hostname and rendezvous
hashing; it is the same behavior as persistent=true. An explicit
strategy takes precedence over persistent. This does not implement
mihomo's source-address sticky sessions or its registrable-domain hash.
smart#
Best = smart, Server 1, Server 2, JP 01, policy-priority="Server:0.9;JP:1.3"
The member real connections do best on.
- Each member has a score: a moving average of how long its connections take to answer, which its tests feed too. A connection that fails before any answer counts as one that took three seconds.
- Members within 30 milliseconds of the best score — or a fifth of the best score, where that is more — are as good as it. A connection goes to one of them — to the one that last did well for the same site, when it is still among them. A site is remembered for an hour.
- A connection that fails to connect is tried on the next members, up to three in all, each given its share of the time a connection has to connect; one that hangs rather than refusing is given up on once its share is spent.
policy-priorityscales scores, by the first pattern a member's name matches:pattern:factor, separated by;, each pattern a regular expression and each factor a positive number. A factor below 1 favours the member; above 1, the reverse. A pair Pipe cannot read is warned about and left out.- A smart group takes proxies alone: groups and built-in policies among its members are left out.
- It tests its members every five minutes at most, when used;
intervalhas no effect on it, and a note says so.
subnet#
Home = subnet, default = Proxy, SSID:Home Wi-Fi = DIRECT, SSID:Office* = Work, TYPE:CELLULAR = Auto
The member for the network the device is on. Each condition is a
subnet expression and the policy it
picks; the first condition that holds decides, and default is used when
none does.
| Condition | Holds |
|---|---|
SSID:Home* |
on Wi-Fi whose name matches, in its case; * and ? are wildcards |
BSSID:aa:bb:cc:dd:ee:ff |
on Wi-Fi with that access point, in any case; * and ? work too |
ROUTER:192.168.1.1 |
on a network whose router is that address |
TYPE:WIFI, TYPE:WIRED, TYPE:CELLULAR |
on that kind of network |
MCCMNC:001-01 |
on that carrier's cellular network, not on Wi-Fi (00101 is the same) |
Every key of a subnet group that is not one of its parameters is a
condition. One without a prefix (Home = DIRECT) is none: it is warned
about, and left out.
defaultnames the policy for every other network. A subnet group without one is warned about, and its first condition's policy stands in.- A condition whose policy cannot be used, or that Pipe cannot read, is left out, with a warning.
- A member can be picked by hand in the app, in place of what the network would pick; the pick holds until it is lifted.
- Of the other parameters, a subnet group takes
hidden,icon-urlandcategory, andpolicy-pathas any group does.underlying-proxyon one is noted and read past, and so, without a word, are the other group parameters.
What a network is — its kind, its router, its Wi-Fi name where iOS gives it — is what the app last said of it, connection by connection.
Members#
How members are assembled#
A group's members come, in this order, from:
- the members its line names, in order;
include-other-group: each group it names, in turn, with all of that group's own members, assembled the same way;include-all-proxies=true: every proxy of the profile's own[Proxy]section, in order;policy-path: the proxies of the list it names, in order.
policy-regex-filter keeps, of steps 2 to 4, the members whose names it
finds; the members the line names are kept as they are. A filter Pipe
cannot use is an error. A name that comes
twice keeps its first place.
policy-regex-exclude-filter removes names matching its regular
expression from all four sources, including explicitly listed
members. Its pattern is limited to 1 KiB and its compiled form to 256 KiB.
policy-type-exclude-filter excludes protocol or group types, separated
by |, without regard to case: for example, http|ss. The types are
those proxy and group lines name; an unknown type is an error. These exclusions also apply to refreshed
subscription contents and to members inherited through another group.
Excluding a group checks that group's type, not the type of its current
selection. A group left empty still refuses traffic.
[Proxy Group]
Nodes = select, Server 1, JP 01, US 01
Picked = select, DIRECT, include-other-group=Nodes, policy-regex-filter=Server|JP
# Picked: DIRECT, Server 1, JP 01
policy-path#
policy-path names a list of proxies — a file next to the profile, or a
URL — whose proxies become the group's members.
Provider = select, policy-path=https://example.com/subscription?token=abc, update-interval=43200
Mine = url-test, policy-path=pool.list, policy-regex-filter=^(JP|SG)
The list may be in any format Pipe imports proxies from. It is told by its content:
| Format | What the list is |
|---|---|
| Profile lines | [Proxy] lines, one proxy each |
| A whole profile | a profile whose [Proxy] section is used, with the [WireGuard <name>] sections its proxies name |
| Clash or mihomo | a configuration's proxies: list, or a proxy provider's file |
| sing-box | a configuration's proxies |
| Share links | one per line — ss://, vmess://, trojan://, vless://, hysteria2:// (or hy2://), tuic://, anytls://, socks5://, wireguard:// — plain, or wrapped in base64 as subscriptions serve them |
| WireGuard | a wg-quick configuration: [Interface] and [Peer] |
A list is read as profile lines when it has a [Proxy] section, or lines
that are [Proxy] lines; anything else is read as one of the other
formats. An entry Pipe cannot use is noted, with why; a list it cannot
read at all brings no proxies, and is warned about.
- Lists at URLs are downloaded by the app as soon as the profile is
opened, kept, and fetched again every
update-intervalseconds — a day unless the group says — while the profile is in use, as a managed profile is: when the app comes to the front, in the background time iOS gives it, and when the tunnel comes up. A list several groups name is fetched once, at the shortest of their intervals. - A list that is not there yet — a URL not downloaded, a file that is missing — brings no proxies, and says so.
- Names. A proxy from a list whose name is taken already — by a proxy or a group of the profile, or by a proxy of another list — is left out, with a warning. A list that several groups name brings the same proxies to each.
- Tailnets are accounts, not proxies a list brings: a list's
tailnetpolicies and Tailscale nodes are left out, with a note. - Sections. A
[WireGuard]section a list brings whose name the profile (or another list) has already is taken in under a name of its own —Home (pool.list)— with a note, and the list's proxies go on naming it: no proxy of a list is built with another's keys and peers. pool.listis the proxy pool: every proxy you imported, as the app hands it to the profile.update-intervalon a group withoutpolicy-pathis noted: it changes nothing.
What a list's proxies are called, and what they are set to#
| Parameter | Values | What it does |
|---|---|---|
external-policy-regex-filter |
regular expression | Keep list entries by their original names, before prefixes and modifiers. |
external-policy-regex-exclude-filter |
regular expression | Remove list entries by their original names, before prefixes and modifiers. Both external filters have the same 1 KiB pattern and 256 KiB compiled limits as the group exclusion filter. |
external-policy-dialer-proxy |
a policy, or DIRECT |
Override the dialer on each imported definition before groups inherit its members. Groups sharing a list with different dialers receive distinct copies; DIRECT removes a list entry's original proxy chain. |
external-policy-name-prefix |
text | Put in front of the name of each proxy the list brings: external-policy-name-prefix="A · ". |
external-policy-modifier |
"key=value, key=value" |
Set on each proxy the list brings, in place of the proxy's own value: external-policy-modifier="tfo=true, test-url=http://www.gstatic.com/generate_204". |
A name prefix also follows chains between entries in the same list, including
ShadowTLS carriers. When a group's modifiers create copies, their chains use
that group's copies too. References to policies outside the list keep their
names; an explicit external-policy-dialer-proxy still names a policy in the
profile.
Testing#
Automatic groups test their members to choose among them.
- A group tests its members when it is used and its last result is older
than
intervalseconds — 600 unless it says, and half a minute at least; for a smart group, five minutes — never on a timer of its own. An idle group is not woken. - A group whose member in use has failed to connect again and again since its last test is tested on the next use, ahead of its interval.
- A test in which no member answered — the device was offline for a moment — stands for half a minute, then twice as long each time again, up to the interval; one that found a member answers stands for the interval.
- Groups share a member's test only when its URL, timeout and accepted HTTP statuses agree. A test asked for by hand waits for the same test under way rather than dialling again.
- Until its first test, a group hands out its first member — a
load-balance group, which has no one member, spreads over them all.
With
evaluate-before-use=true, the first connection waits for the test instead, and fails if no member answered. - A member's test may take
timeoutseconds — the group's, or elsetest-timeout, 5 unless it says; a member whose test takes longer is given up on and counts as not answering. - A member is tested with its own
test-urlandtest-timeoutwhere it has them, otherwise with the group'surlandtimeout, and thenproxy-test-url.DIRECTis tested withinternet-test-urlwhen the profile has one. expected-status=204requires that final HTTP status. Slash-separated statuses and inclusive ranges work too:200/204/300-399.*, the default, accepts every valid HTTP status from 100 through 599. A mismatching response counts as a failed test, including a mismatch in the second request; arbitrary non-HTTP bytes do not count as an answer. Up to four informational responses may precede the final response, each head within 16 KiB, all within the probe deadline.
How a test is timed: it connects through the member and asks for its URL twice on that one connection, and what counts is how long the second answer took. The dial, the proxy's handshake and session, and its own connection to the site — which a test after the first finds made already — are left out, so that the first test measures what each one after it does. A server that answers only once is measured by that one answer. The app's Test measures the same way, and a UDP test is timed from the first time it asks — asking again each second until an answer to its question comes — the relay's session set up first.
Choosing by hand#
An automatic group can be overridden in the app: it then uses the member picked, and is not tested by being used, until the pick is lifted. A subnet group can be overridden the same way. A pick made by hand is kept when the profile is reloaded.
For select and url-test, interrupt-existing-connections=true ends
existing TCP streams and UDP sessions when the selected member changes,
including nested groups and dials still in progress. The next connection
uses the new selection. Choosing the same member again keeps connections
open. The default is false, which lets established connections finish
through their original member. The setting also applies when a URL test
selects a different member automatically. Engine-internal DNS and proxy-chain
connections always follow group changes, even when the option is false.
underlying-proxy#
underlying-proxy on a group takes every proxy among its members —
named, or brought by policy-path, include-all-proxies or
include-other-group — through the policy it names, in place of the
proxy's own underlying-proxy.
[Proxy Group]
Relay = select, Server 1, JP 01
Exits = url-test, policy-path=https://example.com/exits.list, underlying-proxy=Relay
- A proxy is one proxy to the rules and to other groups, so each member
taken this way becomes a policy of its own, named after the proxy and
the way:
US 01 (via Relay). It is tested as the proxy it is made from, and shared by every group that takes the same proxy through the same policy. A pick made in aselectgroup before follows the proxy to its new name. - Members that are groups or built-in policies, the underlying policy itself, and a member that goes that way already stay as they are.
- A WireGuard or
tailnetmember is one connection to its peers, and is not copied: it goes the group's way itself when no rule and no other group uses it as it is, and keeps its own way otherwise, with a warning. Give such a memberunderlying-proxyon its own line instead. - An
underlying-proxythat is not a policy, or that refuses everything, is an error. A group cannot take its members through itself. - On a subnet group it is noted, and read past.
external-policy-dialer-proxy takes only the proxies the
group's policy-path brings through a policy, and leaves its other
members as they are.
Showing groups in the app#
| Parameter | Values | What it does |
|---|---|---|
hidden |
true, false |
The group is left out of the app's lists. It still works. |
icon-url |
a URL | An image the app shows beside the group's name. |
category |
text | Groups of one category are shown together, under its name: the groups without a category first, then each category where its first group stands. |
no-alert |
true, false |
Accepted; it changes nothing. |
Groups that contain each other#
Groups that contain each other in a loop — A a member of B and B of
A — refuse everything, with a warning that names the loop. Groups that
include each other with include-other-group in a loop are an error.
Parameters#
| Parameter | Types | Values | Default |
|---|---|---|---|
policy-path |
every type | a file or URL | none |
update-interval |
with policy-path |
seconds | 86400 |
include-other-group |
all but subnet | group names, separated by commas, quoted: "A,B" |
none |
include-all-proxies |
all but subnet | true, false |
false |
policy-regex-filter |
all but subnet | a regular expression | none |
policy-regex-exclude-filter |
all but subnet | a regular expression, applied to every member source | none |
policy-type-exclude-filter |
all but subnet | types separated by | |
none |
external-policy-name-prefix |
with policy-path |
text | none |
external-policy-dialer-proxy |
with policy-path |
a policy, or DIRECT |
keep each list entry's own dialer |
external-policy-regex-filter, external-policy-regex-exclude-filter |
with policy-path |
regular expression over the original list name | none |
external-policy-modifier |
with policy-path |
"key=value, …" |
none |
interval |
url-test, fallback, load-balance |
seconds | 600 |
timeout |
automatic groups | seconds | test-timeout |
url |
automatic groups | HTTP or HTTPS test URL | proxy-test-url |
expected-status |
automatic groups | *, HTTP status, or slash-separated statuses/ranges |
* |
tolerance |
url-test |
milliseconds | 100 |
evaluate-before-use |
automatic groups | true, false |
false |
interrupt-existing-connections |
select, url-test |
true, false |
false |
persistent |
load-balance |
true, false |
false |
strategy |
load-balance |
random, round-robin, consistent-hashing |
follows persistent |
policy-priority |
smart |
"pattern:factor;…" |
none |
underlying-proxy |
all but subnet | a policy | none |
hidden, icon-url, category, no-alert |
all | as above | none |
default |
select, subnet |
a member or subnet policy | first member |
Parameters Pipe does not know are noted and read past.