Pipe Manual

Profile manual

Groups

On this page
  1. The line
  2. Types
  3. Members
  4. Testing
  5. Choosing by hand
  6. underlying-proxy
  7. Showing groups in the app
  8. Groups that contain each other
  9. Parameters

[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-priority scales 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; interval has 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.

  • default names 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-url and category, and policy-path as any group does. underlying-proxy on 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:

  1. the members its line names, in order;
  2. include-other-group: each group it names, in turn, with all of that group's own members, assembled the same way;
  3. include-all-proxies=true: every proxy of the profile's own [Proxy] section, in order;
  4. 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-interval seconds — 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 tailnet policies 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.list is the proxy pool: every proxy you imported, as the app hands it to the profile.
  • update-interval on a group without policy-path is 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 interval seconds — 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 timeout seconds — the group's, or else test-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-url and test-timeout where it has them, otherwise with the group's url and timeout, and then proxy-test-url. DIRECT is tested with internet-test-url when the profile has one.
  • expected-status=204 requires 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 a select group 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 tailnet member 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 member underlying-proxy on its own line instead.
  • An underlying-proxy that 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.