#
# Shater desired-state config (/etc/config/shater) — v0.2.
#
# This shipped default is intentionally INERT: globals.enabled='0' means the
# daemon (`shaterd run`) stays inert, opens no engine and touches no networking,
# so a fresh install cannot affect connectivity. Configure via the LuCI app (or
# uci), set enabled='1', then apply (the daemon reads this file directly — the
# procd reload trigger restarts it, or `shaterd reconcile` re-reads live).
#
# ONE Go binary, shaterd, embeds the sing-box engine + control-plane + an
# in-process DNS server and renders the nft table `inet shater` + policy routing
# from this file. There is no separate xray/dnsmasq and no generated run.json.
#
# Full schema: docs-shater/PORTING.md (PART A "uci.go — /etc/config/shater
# schema") and shater/model. This file is installed as a conffile — your edits
# survive package upgrades.
#

config globals 'globals'
	option enabled '0'
	option loglevel 'warning'
	# closed = fail-closed (block on engine loss); open = fail-open (plain routing).
	option kill_switch 'closed'
	# There is no dns_mode option: routing is decided by in-engine rule-sets and
	# fake-IP is a resolver type (`config resolver` with type=fakeip + pool).
	#
	# Force ALL LAN plaintext DNS (:53) into the engine, INCLUDING queries the
	# client sends to the router itself (the address DHCP hands out). ON by
	# default: with it off, a client using the router as its resolver is answered
	# by dnsmasq and forwarded to the ISP in the clear — no blocklists, no
	# per-device DNS rules, no resolver detour — while a client that hard-codes
	# 8.8.8.8 IS intercepted. The obedient client leaked; the evader did not.
	#
	# Set to '0' to opt out (dnsmasq answers router-addressed :53 again). Your
	# explicit value is never overwritten: this file is a conffile, and the daemon
	# always writes the option back as '1'/'0'.
	#
	# .lan and the private reverse (PTR) zones keep working: with at least one
	# `config resolver` present the engine gets a synthetic server pointed at
	# dnsmasq on 127.0.0.1:53 plus a rule that sends those suffixes to it; with no
	# resolver at all the engine falls back to the system resolver, which is
	# dnsmasq too. If you changed dnsmasq's domain away from `lan`, add a
	# `config dns_rule` for it (only `lan` + RFC6303 reverse zones are built in).
	#
	# While the engine is DOWN the LAN is NOT left without DNS: the fail-closed
	# holding plane hooks `forward` only, so dnsmasq still answers router-addressed
	# :53 — unfiltered and in the clear, the documented trade-off (blocking it
	# would also cut the daemon's own name resolution and its chance to recover).
	# Queries aimed at an EXTERNAL resolver are dropped with the rest of the LAN's
	# forwarded traffic.
	option dns_intercept '1'
	option ipv6 '1'
	# Reserved fwmark base and routing-table base (do not overlap fw4/other apps).
	option fwmark_base '0x2000'
	option table_base '0x2000'
	# Seconds to auto-rollback an unconfirmed apply (0 = commit-confirm off).
	option confirm_timeout '0'
	option schema_version '1'

# LAN interception inbound. `network` is a UCI interface name; shaterd resolves
# it to its device (e.g. 'lan' -> br-lan) for the nft TPROXY plane. Enable
# globals above and adjust `network` to the interface(s) you want proxied.
config inbound
	option name 'lan'
	option enabled '1'
	option type 'tproxy'
	option network 'lan'
	option tproxy_port '12345'
	option tcp '1'
	option udp '1'
	option sniff '1'

# --- Commented examples (copy, uncomment, adjust, then enable globals) -------
#
# A proxy node from a share link (vless/vmess/trojan/ss/wireguard://...).
#config node
#	option name 'my-node'
#	option enabled '1'
#	option uri 'vless://uuid@host:443?security=reality&pbk=...&sni=example.com#my-node'
#
# A group balancing several nodes (strategy: leastping|random|roundrobin|
# failover|single). Source 'manual' lists nodes explicitly; 'subscription'
# pulls a subscription's nodes.
#config group
#	option name 'main'
#	option source 'manual'
#	option strategy 'leastping'
#	list node 'my-node'
#
# A routing rule. target: chain:<n>|group:<n>|node:<n>|egress:<n>|direct|block.
# Match on src / dst_ruleset / dst_port / proto. A rule with NO matcher at all is
# the default route for everything that reached it.
#
# WHERE the traffic is going is named ONLY by dst_ruleset — one or more
# `config ruleset` names; the rule matches when ANY of them matches. There is no
# inline domain or address list on a rule (`dst_domain`/`dst_ip` were removed in
# schema v2): a destination list is written once as a ruleset, compiled into a
# .srs and shared by every rule that references it. `shaterd migrate` converts
# older configs automatically, creating a `rule-<name>` ruleset per rule.
#config ruleset
#	option name 'blocked-video'
#	option type 'domain'
#	option source 'inline'
#	list entry 'youtube.com'
#	list entry 'suffix:googlevideo.com'
#
#config rule
#	option name 'video-via-main'
#	option enabled '1'
#	option order '50'
#	list dst_ruleset 'blocked-video'
#	option target 'group:main'
#
#config rule
#	option name 'all-via-main'
#	option enabled '1'
#	option order '100'
#	option target 'group:main'
#
# A native DPI-bypass egress (D13). type 'direct' sends traffic straight out (no
# tunnel), and `dpi` applies a compiled sing-box desync on the ClientHello:
#   off      - none (default)
#   fragment - tls_fragment (split the TLS record; the usual light bypass)
#   record   - tls_record_fragment (alternative; mutually exclusive w/ fragment)
#   spoof    - tls_spoof (inject a decoy ClientHello; needs root NET_RAW/NET_ADMIN)
# Point a rule's target at it for DPI-blocked-but-not-IP-blocked domains — direct
# and fragmented, no exit node, no extra binary. (The stronger external `byedpi`
# preset is Phase-2b.)
#config egress
#	option name 'frag'
#	option type 'direct'
#	option dpi 'fragment'
#
#config ruleset
#	option name 'youtube'
#	option source 'geosite'
#	list category 'youtube'
#
#config rule
#	option name 'youtube-fragment'
#	option enabled '1'
#	option order '50'
#	list dst_ruleset 'youtube'
#	option target 'egress:frag'
#
# A DNS resolver (type: doh|dot|plain|local|fakeip). `detour` routes its queries
# through a group/node so DNS does not leak.
#config resolver
#	option name 'cloudflare'
#	option type 'doh'
#	option address 'https://1.1.1.1/dns-query'
#	option detour 'group:main'
#
# --- DNS filter (D15) -------------------------------------------------------
# Network-wide domain blocking, built on sing-box rule-sets + reject DNS rules.
# Turn it ON by setting `option dns_filter '1'` in `config globals` above (it is
# OFF by default). Filtering needs at least one `config resolver` (the in-engine
# DNS plane). A blocklist answers matched domains with NXDOMAIN; an allowlist
# always OVERRIDES the blocklists (allowlisted domains resolve normally).
#
# A blocklist. source: inline|file|url|geosite.
#   inline  - domains listed here (bare domain => also blocks its subdomains;
#             'full:host' exact, '.suffix' suffix, 'keyword:str' substring).
#   url     - a remote sing-box rule-set (.srs); sing-box fetches + caches it
#             itself on the given update_interval (default 24h), via `direct`.
#   file    - a local compiled .srs / source rule-set at `path`.
#   geosite - inert until geodata is shipped (fail-open).
# response: nxdomain (default) | zero  (zero currently falls back to NXDOMAIN).
#
# Well-known lists (url source, shipped DISABLED — flip enabled to '1'):
#config blocklist
#	option name 'stevenblack'
#	option enabled '0'
#	option source 'url'
#	option url 'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts.srs'
#	option response 'nxdomain'
#	option update_interval '24h'
#
#config blocklist
#	option name 'oisd'
#	option enabled '0'
#	option source 'url'
#	option url 'https://raw.githubusercontent.com/sjhgvr/oisd/main/domainswild2_small.srs'
#	option response 'nxdomain'
#	option update_interval '24h'
#
#config blocklist
#	option name 'adguard'
#	option enabled '0'
#	option source 'url'
#	option url 'https://raw.githubusercontent.com/ph00lt0/blocklist/master/adguard.srs'
#	option response 'nxdomain'
#	option update_interval '24h'
#
# An inline blocklist (small, hand-curated):
#config blocklist
#	option name 'my-blocks'
#	option enabled '0'
#	option source 'inline'
#	list entry 'ads.example'
#	list entry '.doubleclick.net'
#	list entry 'keyword:telemetry'
#
# An allowlist (overrides every blocklist above):
#config allowlist
#	option name 'my-allows'
#	option enabled '0'
#	option source 'inline'
#	list entry 'analytics.mycompany.example'
