Every install recipe walked the reader through `shaterd apply` + `shaterd confirm` as if commit-confirm were armed. It is not: DefaultGlobals() never seeds ConfirmTimeout, the shipped config carries confirm_timeout '0', and ArmRollback returns at once on a non-positive timeout. A reader following the README believed an apply that cut their SSH would undo itself. It would not. README/README.en/INSTALL now arm it in the recipe and say what 0 means; the apply-flow diagram gained the edge it always took on a stock box. The boot armor was documented nowhere at all (`grep -rli armor --include=*.md` returned zero) while shipping enabled and blocking LAN->WAN on every boot. INSTALL 4 now says what it is, why SSH/LuCI stay up on purpose, every condition under which it refuses to arm, and how to switch it off. Also removed or corrected, each checked against the code, not inherited: * MASQUE/CONNECT-IP is advertised in both READMEs and absent from parse, generate and model -- registry names it among the types deliberately left unregistered. Dropped, with the fork-vs-product distinction spelled out. The inverse too: Hysteria2/TUIC/XHTTP were tagged [T1] while shipped under with_quic/with_xhttp; ShadowTLS is generate+registry only, no parser. * `direct (flow-offload on)` -- no offload/flowtable/flow_offloading anywhere in openwrt/, shater/ or panel/src. The product does not do this. * shater-core deps were two releases stale in two places, one of which vouched for a config.buildinfo check that never covered kmod-tun. Ruling narrowed to what was actually checked. * PORTING's "Full schema" -- the shipped config points at it -- was missing l3_tunnel and untunnelable_egress (UCI is their only path; the panel does not show them) and the blocklist/allowlist/device/alert sections, while listing a `config preset` that ReadUCI has no branch for. * ARCHITECTURE had no L3 ingress and no kernel egress at all, though both are [MVP] and one creates an fw4 zone in the user's firewall config. New 3a. * nftset-for-routing in the DNS diagram: that is the v0.1 mechanism, gone in v0.2. * CONTEXT described a pre-Phase-1 repo and a 24.10.3 testbed. The testbed is ImmortalWrt 25.12.1 r37978-cd0a06bfd3fd (read off the box), which is not a detail: .apk does not install on 24.10 at all. * The gate existed and no .md mentioned it. README/README.en/CONTEXT now do. * release.yml's header still described publishing as either/or after the rolling pointer became unconditional. Comment only. * Shipped /etc/config/shater: schema_version '1' against CurrentSchemaVersion=2; a pointer to a dns_filter line that was not in the globals block (added, '0'); and `option sniff '1'` on the inbound -- an option the model deliberately does not have, which the first panel save would have silently washed out. * lx-changelog pointed at a D25 heading that does not exist. * ROADMAP 2b and 5 were done and unmarked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BHw89tdWddzhjUc4bAH4tS
9.4 KiB
9.4 KiB
Feature list
The full intended feature set for shater v0.2. Tags: [MVP] target the first
usable release, [T1] next, [T2] later. Phases refer to ROADMAP.md.
Proxy engine & protocols (from the sing-box fork)
- [MVP] VLESS, VMess, Trojan, Shadowsocks, WireGuard, Reality/XTLS.
- [MVP] AmneziaWG 2.0 (I1–I5 CPS decoy packets) — a driving requirement.
- [MVP] Hysteria2, TUIC (
hysteria2:///hy2:///tuic://,shater/parse), XHTTP transport — all shipped: the router tag set carrieswith_quicandwith_xhttpandshater/registryregisters them (scripts/router-tags.sh,buildtags.Features). - [T1] ShadowTLS — half-built:
shater/generateemits it andshater/registryregisters it, but no parser produces one (there is noshadowtls://share link and no subscription path), so a config cannot reach it today. - NOT SHIPPED MASQUE/CONNECT-IP (Cloudflare WARP).
masqueappears nowhere inshater/parse,shater/generateorshater/model, andshater/registrynames it among the upstream types it deliberately does not register (~6 MB of binary and resident RAM). The engine fork can build it; this product does not. - [MVP] Transports: TCP/WS/gRPC/HTTPUpgrade/H2/QUIC as upstream provides.
Transparent proxying & routing
- [MVP] TPROXY transparent proxy for multiple LAN interfaces (TCP + UDP), SNI/ Host/QUIC sniffing.
- [MVP] L3 ingress for ICMP (
globals.l3_tunnel, opt-in, default off): LAN ping travels THROUGH the tunnel instead of being dropped or answered by a forged local reply. The engine opens a dedicated TUN (shater-l3, gVisor stack,auto_routeoff); nft marks LAN icmp/icmpv6 only and a scopedip ruleroutes it in — the TPROXY plane and the main routing table stay untouched (D25). Carried only by L3-capable egresses (WireGuard/AmneziaWG, direct); ICMP routed to vless/vmess/… is honestly dropped, never faked. Ceiling is upstream sing-tun's: ICMP echo only — Windows tracert works, IPv6 traceroute shows just the destination; ESP/AH/GRE/IGMP stay with theuntunnelablepolicy (D17) unlessuntunnelable_egresscarries them (D26). - [MVP] Kernel egress for untunnelable protocols
(
globals.untunnelable_egress, opt-in, default empty): names an existing interface/tunnel egress, and IPsec (ESP/AH), PPTP/GRE, SCTP — everything that is neither TCP nor UDP, plus ICMP when the L3 ingress is off — is routed out that egress's device by the KERNEL with kernel NAT, reusing the egress's own fwmark/table fromaddEgressRouting; the proxy never sees a byte, which is why every protocol works (D26). What that buys depends on the device: a WireGuard interface really is a tunnel, a second WAN is just another uplink whose real address the destination sees. It does not revive multicast IPTV, and UDP-based VPNs (WireGuard, OpenVPN-UDP, IPsec NAT-T) never needed it — they follow the routing rules as before. Theuntunnelablepolicy (D17) keeps only the failure case: a route that did not come up. - [MVP] First-match routing rules by source (IP/CIDR/MAC/interface/zone),
destination, port, proto → target (outbound/selector/chain/direct/block) + egress.
A rule names its destination through a rule-set only — a reusable named list
(inline domains/CIDRs, a local or remote file, or a geosite/geoip category) that is
compiled once into a
.srsand shared by every rule that references it. Domain entries takefull:(exact),suffix:/ a leading dot (host + subdomains),keyword:(substring) andregexp:; a bare entry means host + subdomains. - [MVP] Node groups with balancer/observatory (least-ping/failover/round-robin).
- [T1] Multi-hop chains (L1→Ln); per-rule egress selection; egress via any interface/tunnel (e.g. an AmneziaWG tunnel).
- [T2] Per-destination latency-based routing; auto route-optimization.
Subscriptions & nodes
- [MVP] Subscriptions (VLESS/VMess/Trojan/SS/WG/AmneziaWG), Clash/sing-box/
Xray-JSON formats, per-sub update interval + manual + on-boot; HAPP-style fetch
(UA/HWID/headers); stable per-node identity (fingerprint reconcile) across
refreshes; quota/expiry from
subscription-userinfo. - [MVP] Manual nodes: paste share-link(s), file import, or a wg-quick/
AmneziaWG
.conf. - [T1] Node health test (TCP + real proxy-path HTTP probe, exit-IP), "test all", QR export.
DNS, filtering & blocking (a core value layer)
- [MVP] :53 hijack, in-process sing-box DNS; per-domain resolver selection;
DoH/DoT/plain resolvers; fake-IP as a resolver TYPE (
config resolvertype=fakeip + pool — there is no global "FakeIP mode"); no DNS leaks. Routing is decided by in-engine rule-sets — the v0.1 dnsmasq→nftset population mechanism does not exist in v0.2 (see generate/dns.go). The hijack covers the queries a client sends to the router itself — the address DHCP hands out — becauseglobals.dns_interceptis ON by default (D24). With it off, those queries go to dnsmasq and out to the ISP in the clear, so the well-behaved client leaks while the one that hard-codes 8.8.8.8 does not..lanand the private PTR zones are preserved through dnsmasq either way. Two things the promise does NOT cover, both by design: while the engine is DOWN the holding plane hooksforwardonly, so dnsmasq still answers router-addressed :53 unfiltered (client traffic and DNS to external resolvers stay blocked); and with noconfig resolverat all there is no DNS plane to filter with — queries fall through to the system resolver and generate says so. - [MVP] Client DoT/DoH blocking (stop devices bypassing the filter).
- [MVP] Blocklists with flexible sources:
inline(type your own) /file/url(auto-update) /geositecategory (only when geodata present). Aurllist may be a hosts file, a plain domain list or an AdBlock-style||domain^list — the formats StevenBlack/OISD/AdGuard/hagezi actually publish — and is compiled to a local.srson the router; a URL already serving.srs/.jsonis used directly. Response NXDOMAIN or 0.0.0.0; allowlist overrides. - [MVP] Efficient matching for large lists: compiled succinct-set matcher
(
.srs, zlib) with dedup, refreshed onupdate_interval— not dnsmasq megalists (seeDECISIONS.mdD5). A compiled list costs ~1% of the source text on disk (StevenBlack ≈ 150k domains → ~80 KiB) and nothing at steady state. Ceiling: 200k domains perurllist, set by the RAM the one-off compile needs on the target hardware (~116 MiB peak at 150k, linear; 512 MB total). Larger lists are refused with a message pointing atgeositecategories, which are pre-compiled upstream and cost no memory to build. Subdomain-collapse and a bloom prefilter are NOT implemented. - [T1] Safe-search enforcement; category-based blocking bundles.
Per-device control & parental
- [T1] Devices page: auto-discover (dhcp.leases + ip neigh), name devices, live status/traffic.
- [T1] Per-device toggles: proxy on/off, blocklists on/off, exit country/node.
- [T1] Per-device domain block/allow (block a site for one device or everyone).
- [T2] Schedules: time-windowed rules (bedtime, school hours) per device/group.
- [T2] Per-device data quotas.
Statistics & visibility (a core value layer)
- [T1] Per-domain stats: top queried/blocked domains, allowed-vs-blocked, per-device breakdown, timelines — fed by the engine's in-process DNS events.
- [MVP] Per-client / per-node / per-rule traffic (bytes), from nft counters + engine stats.
- [T1] Live query log (streaming) with one-click block/allow.
- [T2] Connection inspector; Sankey/leaderboard views; geo-map of exits.
Reliability ("железно")
- [MVP] Fail-closed kill-switch (dead group → block, never silent direct leak); IPv6 dropped when disabled.
- [MVP] Atomic apply with engine +
nft -cvalidation. Commit-confirm auto-rollback to last-good is built and works, but it is opt-in and ships OFF:DefaultGlobals()leavesConfirmTimeoutat 0, the shipped/etc/config/shatersaysconfirm_timeout '0', andapply.ArmRollbackreturns at once on a non-positive timeout. Until an operator sets a window, an apply on a stock box has no net under it — andshaterd applysays so. - [MVP] Idempotent reconcile from hotplug/boot under flock; restart engine only on real config change; management-bypass (SSH/LuCI/LAN) always exempt.
- [MVP] Own nft table
inet shater+ own marks/tables; never touch fw4.
UI — thin LuCI + full admin panel
- [MVP] Thin LuCI app: pretty mini-dashboard (status + throughput) + "Open
panel" button with short-lived token handoff (see
ARCHITECTURE.md§2). - [MVP] Admin panel (SPA, own port, embedded in the binary): overview, node/subscription management, routing rules, apply/rollback, DNS/blocklists.
- [T1] Rich stats dashboards, devices page, live query log, config diff/history.
- [T2] Named profiles/scenes; WAN-mode profiles (conditional overrides, e.g. SIM uplink → different egress); backup/restore; i18n (EN + RU).
Ops & distribution
- [MVP] Single signed binary; signed apk feed on Gitea (EC key
dist/shater-apk.pem); one-line install; named-packageapk upgrade. - [T1] Upstream-rebase cadence (track sing-box-lx tags) with a smoke suite.
- [T2] Multi-router fleet management; REST/gRPC external API; Telegram bot. (apk packaging landed and is now the only lane — D22.)