group { ... } configures group members and selection policies. A configuration may define at most 250 top-level user groups; higher routing ordinals are reserved by the ABI.
Each group is a named subsection of group { ... }:
group {
hk {
filter: subtag('airport') && name(keyword: 'HK')
filter: name(regex: '^Hong Kong ')
policy: min_moving_avg
check_url: 'https://www.gstatic.com/generate_204'
final: direct
}
proxy {
filter: group('hk')
filter: name('backup')
policy: select
default: 'hk'
final: direct
}
}| dae key | Internal field | Default | Meaning |
|---|---|---|---|
| (section name) | name |
required | Group tag used as an outbound in routing and APIs. |
policy |
policy |
selector |
Member-selection policy; accepted spellings are listed below. |
icon |
icon |
null |
Validated absolute HTTP(S) URL or data URI, at most 2048 characters; returned by native group reads with listener-secret values of at least 8 bytes masked, never guessed. |
filter: name(...) |
filters + nodes |
[] |
Select nodes by node name. The parser resolves matches to node UUIDs. |
filter: subtag(...) |
filters + nodes |
[] |
Select nodes by the current tag of the subscription that produced them. |
filter: group(...) |
groups |
[] |
Add nested group tags. Comma-separated arguments and pipe-separated tags are accepted. |
default |
default |
null |
Initial or fallback member tag for selector. |
final |
final_outbound |
null |
Node, group, direct, or block used when the group's policy has no eligible selection, including when that group is nested. Final nodes remain health-gated; missing or cyclic finals fail closed, never implicitly direct. |
check_url |
check_url |
null |
Per-group TCP health-check target for non-Selector policies. A Selector ignores it with a warning. |
| — (not in dae) | check_interval |
null |
Per-group interval field in seconds. The current runtime does not consult it and uses the global interval. |
tolerance |
tolerance |
50 |
Nonnegative URLTest switch threshold in milliseconds. Without an explicit value, dae URLTest groups inherit global.check_tolerance; the runtime applies an effective minimum of 1 ms. |
idle_timeout |
idle_timeout |
null |
Nonnegative URLTest probe-suspension threshold after inactivity, in seconds. With null, the health layer uses 1800 seconds. |
interrupt_connections |
interrupt_connections |
false |
Close affected live userspace transports on selection changes, using captured group identities and network. This is transport retirement, not tracking removal; shared UDP carriers retain unaffected views. |
| — (not in dae) | id |
random UUID | Internal group identity generated when the field is absent. |
| Canonical name | Accepted dae spellings | Behavior |
|---|---|---|
selector |
selector, select, fixed, fixed(0) |
Independently for TCP and UDP, uses that network's runtime choice, then default, then the first existing member before health filtering. Health never replaces a valid choice with a sibling. The choice may be a direct node or nested group. |
urltest |
urltest, min_moving_avg, min_avg10, min_last_delay |
Selects the lowest-latency alive member using the halving moving average (prev + sample) / 2 and tolerance; TCP and UDP selections are independent. |
loadbalance |
loadbalance, roundrobin, round_robin, balance |
Round-robins over alive members with independent counters per group and TCP/UDP network. |
fallback |
fallback |
Pins the first alive member in declaration order independently for TCP and UDP; recovery of an earlier member does not immediately fail back. |
score |
score |
Always compiled; chooses one alive member using observed reliability, fresh target quality and bounded validation. TCP/UDP and target IPv4/IPv6 evidence remain separate. |
Policy matching is ASCII case-insensitive. Before matching, the parser removes any parenthesized suffix, so it accepts fixed(0). An unrecognized policy becomes selector and a diagnostic names the group. Legacy honk is invalid; use score.
Only missing or no-longer-member choices fall through to default or the first declared member. An existing chosen member without an eligible leaf does not fall back to a sibling, on either TCP or UDP: the only continuations are an explicit final or the same-leaf TCP last-resort rule below. A chosen nested group still applies its own policy, so URLTest may select another leaf inside that group.
Each selected subgroup resolves its own explicit final before returning an empty result to its parent. This is not permission to choose a different Selector sibling or retry a terminal protocol refusal. For IPv6 targets, an ordinary selected leaf reachable through IPv4 proxy health is tried before a final route.
UDP eligibility requires protocol/configuration support as well as health. VMess and explicitly TCP-only leaves are excluded before selection and Score comparison; TCP remains unaffected. An incapable Selector choice does not authorize a sibling, and built-in block remains terminal. This capability filter does not bypass a selected node's target-specific UDP policy refusal.
When distinct nodes share a display tag, name-based defaults and Clash writes bind the first matching member in declaration order before health filtering. Native member IDs can select a particular direct member. Health cannot retarget either choice to a healthy same-name sibling.
Named final nodes likewise resolve the first matching configuration declaration; a healthy duplicate cannot replace it. Selection and final-node health registration use that same identity.
Native PUT /api/v1/groups/{groupId}/selection accepts a direct member_id and network: "tcp"|"udp"|"both". Both-network changes validate and publish together; Clash selection writes both and displays TCP. Per-network choices are retained by reload/persistence, and interruption is acknowledged only after actual closes. On an automatic group the same write pins the member until DELETE selection or the next configuration activation clears it.
Restricted native Group PATCH edits parser-owned source spans only when the group's accepted source is writable. It supports policy, default member, final, tolerance, idle timeout, interruption and check URL, but not icon, name or filters. It uses the quoted accepted group/config revision independently of source disk hashes. Full validation, durable write and reload still apply; a write is not activation success. See the group API contract.
If a group has exactly one unique leaf, no final, and that leaf is excluded by TCP health, honk can still dial the same leaf as a last resort, but only if the current Selector choices lead to it. This cannot bypass a chosen empty sub-group or imply a direct fallback. The node remains marked dead until real traffic or probes recover it; UDP keeps normal dead-member exclusion. Last-resort serving logs a rate-limited warning (60s per group).
Every configured Selector TCP proxy leaf stays warm. After resolving its TCP choice, honk retains a reusable multiplexed session, a QUIC client, or one bare server TCP connection according to the leaf protocol; direct and block need no warm resource. The separate UDP warm set follows UDP selection.
policy: score explicitly enables Score; it is always compiled and has no runtime tuning fields. An omitted policy remains selector. Reliability protects admission, but additional historical successes alone do not exclude a less-sampled, validated faster node.
Score chooses one authoritative leaf after ordinary health filtering. Proxy-health family is independent of business-target family. First-choice utility and its WeightedMean performance heuristic remain unchanged; sufficiently observed candidates compare actual failure risk rather than the most-sampled node's lower bound. Sparse evidence gets bounded trials. Configured probe quality supplies a baseline that fresh trustworthy target evidence can override. Equal values use declaration order and stable identity. Healthy-incumbent promotion uses separate bounded comparison evidence, not the heuristic alone.
Optional trials use a budget for each retained (group, TCP/UDP, target family) scope:
- Initial allowance: the scope freezes member count
nand cold allowanceB.Bcovers all members up to four; otherwise it isceil(sqrt(n)) + 1. - Earned credit: each original business start (counted in
N) adds1/q, normally withq = 16. It usesq = 8only while the group's recent original business on that network (five-minute half-life) implies at least two flows per second, and the scope has at least three recent optional-trial outcomes with at least 80% success (ten-minute half-life; decayed weight above 2.5). Evidence needs a fixed number of trials, so busy groups whose trials pay off fund it sooner; quiet groups keep the smaller share. Target refusals do not count as trial outcomes, and reload clears these outcomes. - Low-yield pause: while the ordinary selection is observed usable and every scope behind a read has recent outcomes under 20% success, two or more recent failures pause optional trials until those failures decay. With one recent failure, trials proceed one at a time. Each candidate refused by a paused scope counts in
budgetBlockedand reads as abudgetwait, just like exhausted credit. A member with a trial already in flight still readsinFlight. Without a usable selection, exploration continues so Score can escape; recovery continuations are unaffected. - Budget ceiling: spent trials
Vplus unbegun reservationsRnever exceedBplus the whole earned credit. Unspent earned credit is capped at eight. - Counting:
Nincludes chosen trials, but retries, clones and repeated nested attribution cannot multiply it. The root count deduplicates nested groups. Time, reads, new targets and evidence expiry grant no credit. Retained scopes preserve currency through reload and membership changes. - Spending and refunds: node-specific work spends credit before DNS/admission waits, separately from the physical/logical reporter start. Releasing the last reference before work begins refunds a reservation; cancellation after work begins does not. Recovery retries are separate, budget-neutral work.
- In-flight limit: each member has at most one unanswered trial for its open question on a target. Except for recovery, a member is dispatched only while its completions plus unfinished work stay below the selection's completions.
- Failure handling: trials do not become ordinary incumbents. Real failures retain exponential backoff and three-failure normal exclusion; recovery remains possible after backoff expires.
Business reliability retains its 30-minute half-life. Heuristic performance confidence ages independently and expires after 120 seconds regardless of sample count. Setup and first response publish immediately; accepted transfer progress uses nonoverlapping 1–10 second windows with at least 64 KiB in the measured direction, bidirectional flow progress and a response. Windows never multiply terminal outcomes. Goodput is workload-dependent, not link capacity; silence is not congestion. Mature switching protection remains 0.005 after eight effective completions, using the maximum separately decayed support across applicable layers. Ineligible incumbents and unresolved business failures bypass holding; recovered historical failure mass alone does not.
Only an actual ordinary move to another leaf takes priority over optional validation. A failed incumbent that remains selected does not prevent a funded, actionable challenger trial; this does not clear failure or backoff.
Promotion considers every eligible challenger in the shared serving pool (capacity k is demand-sized, with a √n ceiling for larger groups; every ordinary or trial selection is in-pool, with no extra winner slot; see conditional verification) against one fixed incumbent, using paired exact targets, at most eight canonically selected response-qualified common targets, or the same current configured-probe cohort. Common-target qualification precedes the cap without selecting by measured value; skipped unqualified or capped matched targets leave only a partial comparison, and that member's response gap stays pending. Directional metrics intersect over the same selected response cohort. Business proof uses four shared 15-second blocks expiring at block start plus 60 seconds; configured probes use four blocks of max(15s, 2I) for their actual interval I, with expiry additionally capped by the weaker side's latest support plus max(60s, 2I). Each metric still requires four distinct reporters per side, and changed cadence cannot inherit prior probe support. Directional gain requires at least 10% improvement, no greater than 10% regression in another known direction, response no more than 10% slower, and equal-or-better qualified terminal reliability. Unknown directions stay unknown; opposing changes are reported as a tradeoff rather than hidden by a best-direction score. Paired response and qualified reliability gains still participate in the existing hold-margin decision. No additional Score tuning field is introduced; see the lifecycle design.
Readonly pairwise relations are separate from that switching decision: they compare actual response values with symmetric 10% practical equivalence, not completion maturity or a millisecond floor, and describe response time only; reliability and directional protections stay with switching. A missing pair response requests budgeted comparable business traffic, not bandwidth testing. See conditional verification.
Targeted Traffic RX after setup/TX can open budgeted half-open recovery before a flow ends. Four distinct reporters in the exact cell's uninterrupted post-failure cohort clear its hard episode and grant a current 60-second qualification lease; neither one reply nor another target's recovered aggregate suffices. Historical failures remain in utility and promotion, and no terminal successes are invented. Initial cold qualification still requires four effective useful completions; RX may renew an earned lease but reload alone does not grant recovery. Publication is event-driven, at most once per second per reporter after the first eligible RX. Target-path failures affect only exact-target hard state while retaining numeric aggregate outcomes; explicit carrier/proxy faults and unknown pre-setup failures remain node-scoped and fence dependent evidence. See scoped recovery and optional validation.
Business scores remain scoped by group, transport, target family, normalized target and node identity. Global/family/exact reliability is blended without adding overlapping completions. Target performance overrides its baseline only while fresh and comparable. Probe RTT has separate protocol/family and normalized HTTP URI/method identity; warm-up supplies setup quality only. Nested paths attribute a real outcome once to every traversed Score group.
Transparent TCP/UDP, supported DNS exchanges and UI downloads report actual business attempts. Configured HTTP/UDP/QUIC probes provide fresh quality without adding business successes or clearing traffic streaks. Manual Clash delay measurements retain their Alive/API history but do not feed a configured Score baseline or turn arbitrary target failure into real-dial demotion. Actual preparation remains warm-up evidence. Independent Score QUIC handshakes preserve existing DataUdp health recovery without invented byte volume.
Sustained measured carrier pressure may reopen bounded comparison without adding business failures or directly changing the winner. This is honk-to-proxy advisory evidence, not end-to-end UDP loss or a first-request guarantee; unsupported paths remain unknown. See carrier-pressure scope.
Score state is memory-only and process-local, with one 4,096-entry LRU for exact node-target cells and another 4,096-entry LRU for aggregate cells. Exact-target evidence measures observed transport quality, not whether a service is semantically unlocked; use existing routing or geosite rules to select a dedicated service-specific Score group when that coarse cohort is needed. A successful in-process reload shares the same state and removes cells for deleted groups or members; process restart clears it. Score cells and scorer-only domain/IP keys are never logged, persisted, or returned by Clash APIs. Established connection metadata is unaffected.
The separate comparison store has at most 512 cells and a 1 MiB logical-allocation limit, including owned key and vector capacity. This is not a process RSS limit; allocator overhead and other state are outside it. Missing or evicted support remains unknown.
After a retryable TCP setup failure, a Score-owned request may try one different permitted leaf even if ordinary ranking still prefers the failed node. Both attempts share a deadline and existing dial admission. Selector boundaries and actual primary final-edge provenance remain authoritative; no new direct/final route or application payload replay is introduced. A committed reload invalidates probe baselines while retaining valid in-flight business evidence; neutral cells stay within the existing bounded LRUs.
Each authorized applied multi-candidate rank records one exclusive reason: coldExplore, periodicExplore, incumbentIneligible, freshFailureBypass, insufficientEvidenceHeld, directionalTradeoffHeld, incumbentHeld, reliabilityWinner or performanceWinner. ordinarySwitch counts committed same-scope A→B changes, excluding first choices and trials; switchFlap counts the subset returning to the previous winner within eight ordinary choices. Candidate filtering/backoff counters remain separate. Peek and display/API reads are neutral. Authenticated /stats.score.groups[] exposes fixed group/network reason, verification and budget fields; /stats.score.cache describes evidence and comparison storage. Trial outcomes and setup/elapsed costs are observations, not causal extra failure or overhead estimates. See API reason semantics; no scorer-private node or target identities are exported.
For Clash compatibility, Score remains automatic but is represented as type: "url_test"; now is the current aggregate TCP winner, and PUT /proxies/{name} is rejected.
Sampling resolves availability, response, ordinary qualification and recovery gaps within the shared budget. Fresh equivalent cohorts stop forced sampling once qualification/recovery gaps also close; missing transfer waits for real offered load. scoreVerification distinguishes provisional forwarding, observed usability, fresh pairwise challengers relations, the evidence question and waitReason; aggregate reads inspect retained IPv4/IPv6 waits without spending credit. See API verification. Relations are retractable empirical observations, not a guarantee of best-arm identification or future network behavior.
Each challenger's pair with the selection is judged on its own shared blocks; there is no joint alignment across challengers. One dispatch order serves optional work: untrained members first, then the most progress toward a member's open question, then the least recently selected member. Trials preserve business currency and in-flight bounds and never replay requests. Readonly question, nextAction and waitReason report the first member of that order without exposing target identities.
- A standalone
group('tag')line adds nested tags togroups; it is not evaluated as a node predicate. A nested tag may contribute the leaf selected by that group's current policy. Agroup(...)line containing another predicate or trailing text is reported as a diagnostic and ignored; a group whose only filter is such a line remains empty. name(...)matchesNode.name.subtag(...)mapsNode.subscription_idto the current subscription tag and matches that tag. Plain arguments are exact matches,keyword:is a substring match, andregex:is a raw regular expression. Matching is case-sensitive; multiple arguments in one predicate are alternatives.&&combines predicates on one line with AND. Prefixing a predicate with!negates it. Separatename(...)andsubtag(...)filter:lines combine with OR; standalonegroup(...)lines add nested candidates.- Filter-derived membership is rebuilt after every subscription refresh. Stable node UUIDs therefore do not retain stale membership after their subscription provenance changes.
- A group with neither node filters nor nested groups receives all current nodes. Explicit
group(), an empty filter, an empty nested group, or a filter emptied by subscription refresh selects nothing. Empty contributions remain explicit through JSON/YAML/TOML round-trips and refresh; valid sibling filters still contribute by OR, andfinalremains independent.
A filter honk cannot parse is ignored and reported by its ordinal among the group's node filters, excluding only nonempty standalone group(...) lines; mixed lines are counted. group() emits empty-subgroup. Remove the filter for all nodes; explicit group() now selects none. The injected direct and block are outbounds, not pool members: an unfiltered group, keyword:, regex: and subtag(...) never select them, and only a filter that spells the name exactly (filter: name(direct)) admits one.
Nested selection is depth-capped at 8. When the group manager builds the graph, it removes each cycle-closing edge and logs a warning; an unknown nested tag contributes no candidate. Each nested group contributes the single leaf selected by its own policy, so every dial ultimately resolves to one node.
Clash-facing group output preserves member tags: the all field lists direct node names and nested group tags rather than expanding nested groups. Leaf-facing health and connectivity traversal expands the real nodes below those tags.