Skip to main content

Policy Orchestration

sequence and fallback define branching, sub-chain calls, and primary/fallback execution.

sequence

Purpose

Orchestrates matchers and executors into a pipeline. This is the most common entry executor.

Example Configuration

- tag: seq_main
type: sequence
args:
# Try cache first
- exec: "$cache_main"
# Stop immediately on cache hit
- matches: "has_resp"
exec: "accept"
# Arrays of matches are AND-ed together
# Examples prefer quick-setup matcher expressions directly
- matches:
- "client_ip $lan_ip_set"
- "qname $local_domains"
exec: "$hosts_main"
# Rules may also execute unconditionally
- exec: "$metrics_main"
# Only forward when no response exists yet
- matches: "!has_resp"
exec: "$forward_main"
# Normalize TTL after a response is available
- matches: "has_resp"
exec: "$ttl_main"

Configuration Details

args

  • Type: array; Required: yes; Default: none
  • Purpose: Defines the rule chain.
  • Runtime impact:
    • Rules execute in order.
    • Initialization fails when the array is empty.

args[].matches

  • Type: string or array
  • Required: no
  • Purpose: Match condition for the current rule.
  • Runtime impact:
    • Multiple conditions are combined with logical AND.
    • Omitted means the rule has no precondition.

args[].exec

  • Type: string; Required: no; Default: none
  • Purpose: Action to run when the rule matches.
  • Supports:
    • plugin references
    • quick setup expressions
    • built-in control flow

Behavior

  • Rules run sequentially.
  • A rule with multiple matches requires all of them to be true.
  • Other sequence instances can be called with jump or goto.

Built-In Control Flow

Besides plugin calls, sequence.args[].exec can also use built-in control flow:

accept

  • Ends the current sequence immediately.
  • This is an explicit early stop, so outer callers do not continue with later rules.
  • Does not build a response by itself; it is usually used after an earlier stage has already produced one.

return

  • Ends the current sequence immediately and gives control back to the caller.
  • Does not build a response.
  • If the current sequence was entered by jump, the caller continues with the next rule.

reject [rcode]

  • Builds a response immediately and ends the current sequence.
  • The default rcode is REFUSED.
  • Decimal numeric rcodes and case-insensitive English RCODE names are accepted, for example reject 2, reject SERVFAIL, reject servfail, or reject NXDOMAIN; common mappings and meanings are listed in the DNS Code Reference.
  • Only base DNS RCODEs 0..15 are supported; extended RCODEs require an EDNS OPT and are not generated by this built-in action.
  • reject 0 returns a plain NOERROR response and does not add an SOA automatically.
  • Stops later rules from running.

mark ...

  • Inserts one or more integer marks, then continues to the next rule in the current sequence.
  • Supports mark 1, mark 1 2 3, and mark 1,2,3.

jump seq_tag

  • Calls another sequence; conceptually this is a subroutine call.
  • The parameter must be the target sequence tag without $.
  • If the target sequence reaches its tail or executes return, the current sequence resumes with the next rule.
  • If the target sequence executes accept, reject, or another Stop, the current sequence stops too.

goto seq_tag

  • Transfers control one-way to another sequence.
  • The parameter must be the target sequence tag without $.
  • Once goto runs, the current sequence never resumes at later rules.
  • If the target sequence executes return, that return is propagated outward.

Typical Uses

  • One readable top-level entry.
  • Split cache, local answers, forwarding, and integrations into understandable policy layers.
  • Build complex branches with marks and matchers.
Notes
  • Referenced plugins must already exist.
  • A sequence needs at least one rule.

fallback

Purpose

Runs a primary executor first and falls back to a secondary executor when the primary is too slow or fails.

Example Configuration

- tag: fallback_main
type: fallback
args:
# Preferred path
primary: "forward_fast"
# Backup path
secondary: "forward_stable"
# Let the backup take over after 200 ms
threshold: 200
# Keep the backup running in parallel for lower tail latency
always_standby: true

Configuration Details

primary

  • Type: string; Required: yes
  • Purpose: Primary executor tag.

secondary

  • Type: string; Required: yes
  • Purpose: Secondary executor tag.

threshold

  • Type: integer; Required: no
  • Unit: milliseconds
  • Purpose: Delay before the secondary is allowed to take over.

always_standby

  • Type: boolean; Required: no; Default: false
  • Purpose: Keep the secondary in standby for all requests rather than only after the threshold condition.

short_circuit

  • Type: boolean; Required: no; Default: false
  • Purpose: Stop the executor chain after fallback selects the winning response.

Behavior

  • Provides controlled degradation instead of unconditional double-querying.
  • Useful when one path is usually faster but another path is more complete or stable.
  • With short_circuit enabled, the winning branch writes its response and then immediately stops the remaining executor chain.

Metrics

Exported through the global GET /api/metrics endpoint:

  • fallback_primary_total
  • fallback_primary_error_total
  • fallback_secondary_total

Typical Uses

  • Low-latency primary plus stable backup
  • Tail-latency protection
Notes
  • A too-aggressive threshold can turn the secondary into a routine dependency.