On this page
Practical Home Network Architecture: Wired Mesh, Single-Arm Side Router, and Fail-Open
The goal of this network is not to pile up devices, but to solve three problems simultaneously: stable coverage in multiple rooms, device-based proxy path selection, and preventing the side router from dragging down the entire home network when it fails. The final structure is: Optical modem bridging → Main router PPPoE dial-up → 4 APs with wired backhaul; The N100 single-port device handles transparent proxy and DNS, while the core workstation bypasses the side router and manages its own proxy.
This article serves as both an architectural review and a design specification that you can use to check your own network. Hostnames, SSIDs, subscription addresses, and public nodes in this document have been generalized; .1, .7, .8, etc., are used only to illustrate role relationships within the same private network.
Design Conclusions First
- Mesh nodes should use wired backhaul whenever possible; wireless roaming cannot compensate for congestion in the backhaul link itself.
- Traffic for a single-arm side router enters and exits through the same LAN interface; return path symmetry and ICMP Redirects must be handled separately.
- When DNS, transparent proxy, and DHCP gateway are coupled, a Fail-Open design should be implemented; if the proxy fails, it can degrade gracefully, but it must not cause a network outage for the entire house.
- Key workstations can connect directly to the main router and run their own TUN, isolating the fault domain from the whole-house transparent proxy.
- Configuration should be derived from a single source of truth; target endpoints only maintain their own DNS, TUN, and inbound differences.
1. Physical Topology
The optical modem performs only optical-electrical conversion (bridging, no dial-up, routing, or NAT); the public IP goes directly to the main router's PPPoE interface, avoiding double NAT. The whole house is approximately 120m² with four rooms. A single AP cannot cover all rooms (especially the master bedroom and secondary bedroom, which are separated by two load-bearing walls). Four APs with wired Mesh cover dead zones + unified SSID for seamless roaming. All APs use wired backhaul, not occupying wireless bandwidth.
2. Mesh Network
| Node | IP | Hardware | Location | Coverage Challenge | Backhaul |
|---|---|---|---|---|---|
| root | .1 | RP01 | Entrance | Living Room Center | — |
| leaf | .62 | RC06 | Living Room | Covers Entrance Area | Wired |
| leaf | .67 | RP03 | Master Bedroom | Separated by Two Load-Bearing Walls | Wired |
| leaf | .80 | RP03 | Study | Core Equipment Area | Wired |
| leaf | .227 | RP03 | Secondary Bedroom | Furthest, No Signal from Single AP | Wired |
On the wireless side, one main SSID (5 GHz for daily devices) and one independent 2.4 GHz IoT SSID are used. Whether the IoT network is hidden does not constitute a security boundary; true isolation should rely on guest networks, VLANs, or firewall policies.
3. Key Hosts
| Device | IP | Role | Connection | OS |
|---|---|---|---|---|
| ONT | — (Bridged, No IP) | Optical-Electrical Conversion | Fiber In, Ethernet Out | Telecom GPON |
| Xiaomi Main Router | .1 | PPPoE/NAT/DHCP/Mesh Root | WAN connects to Modem | MiWiFi (RP01) |
| N100 Side Router | .7 | Transparent Proxy + DNS (AdGuard Home) | Wired Single Port, Hangs on Study AP LAN | iStoreOS |
| Core Workstation | .8 | Development & Self-Hosted Services, Static Gateway Points to .1 | Wi-Fi, Connects to Study AP | Linux |
| Old Node | .9 | Retired Side Router, Maintenance Entry Only | Wi-Fi | Windows |
Key Design: The static gateway of the core workstation (.8) points to the main router (.1), unaffected by the side router's transparent proxy. It runs its own TUN-mode mihomo and uses DNS that returns real addresses, thus forming two independent fault domains from ordinary home devices.
4. DHCP and DNS
- Gateway: DHCP defaults to issuing .7 (N100); ordinary devices are forwarded via the side router. The core workstation is an exception, with its static gateway pointing to .1.
- DNS: DHCP only publishes .7 (AdGuard Home on N100). Do not issue public DNS as a "backup" simultaneously, because clients can bypass the primary DNS, making filtering and splitting results unpredictable. Fail-Open temporarily takes over :53 during component failures.
- Lease Time: 1–2 hours. Shorter leases facilitate faster convergence after modifying gateway configurations but cannot replace gateway high availability.
- Address Pool: 192.168.31.5–250.
Normal DNS Path
5. Traffic Path
Normal Mode
① Mainland IPs are returned at the firewall layer, physically not entering mihomo — Hard protection for domestic traffic. The single-port hairpin (entering and exiting via the same br-lan) forwards back to .1, MASQUERADE must change the source to .7, otherwise asymmetric return paths will directly cause network outages.
② fake-ip misses CN domains in geosite (obtaining 198.18.0.0/15 addresses) will still enter mihomo, which can be caught by the
GEOIP,CN,DIRECTrule at the end of the ruleset after resolving the real address. The core workstation uses its own TUN and real-address DNS, not relying on this path.
Fail-Open Mode (mihomo/AdGuard Down)
This Fail-Open has clear boundaries: it relies on daemons still running on the N100, covering only software failures such as mihomo or AdGuard Home exiting or freezing. When the N100 loses power, the kernel hangs, or the LAN link is interrupted, .7 as the default gateway becomes completely unreachable, still causing ordinary clients to lose connectivity. To cover this layer of failure, VRRP/Double-Active Hot Standby is required, or the main router automatically changes the DHCP gateway after health checks; the current architecture does not implement this.
6. Splitting Strategy and Domestic Traffic Protection
A CN domain missed by geosite → MATCH,PROXY → US node reconnecting to a domestic server → GFW identified "domestic traffic" and blocked the VPS IP occurred previously. Now using Dual-Layer Protection:
Layer 1 (Hard, Firewall): bypass_china_mainland_ip=1 (v4) fills the nft china_ip set (~3.7k chnroute). Mainland IPs are returned at prerouting, physically unable to reach mihomo, regardless of whether the domain matches geosite.
- Single-port hairpin requires
table inet bypass_masqto MASQUERADE forwarded traffic source to .7. - v6 firewall bypass is temporarily disabled (to avoid v6 hairpin issues); v6 CN traffic falls back to mihomo
GEOIP,CN.
Layer 2 (Fallback, mihomo Rules): End of ruleset GEOSITE,cn,DIRECT → GEOIP,CN,DIRECT → MATCH,PROXY. CN domains that missed fake-ip enter mihomo, where GEOIP,CN intercepts after resolving the real IP.
Key sysctl
| Parameter | Value | Reason |
|---|---|---|
net.ipv4.conf.*.send_redirects | 0 | Otherwise .7 sends ICMP Redirects, causing clients to bypass .7 and connect directly to .1, forming a "dual path": same target client direct connection + mihomo dial-up coexist, triggering server PAWS to discard the one with the smaller timestamp after NAT via the same public IP on the main router → i/o timeout storm (the real culprit for apps like Xianyu freezing). Applies to all single-arm/hairpin side router solutions, not just this one. |
net.ipv4.conf.*.rp_filter | 0 | Allows asymmetry, cooperating with hairpin |
Outbound Lines
Three VPSs are distributed across different network lines; differences in latency and stability come from the QoS levels of the return lines:
| Line | Full Name | Priority | Characteristics | Suitable For |
|---|---|---|---|---|
| CN2 GIA | China Telecom Next Gen Carrier | Highest | Telecom top-tier line, entire CN2 backbone without passing through 163 network, lowest QoS, basically no speed drop during evening peak | Main nodes requiring low latency/stability |
| 9929 | China Unicom Premium | Medium-High | Unicom premium line, similar to CN2 GIA but via Unicom backbone, optimal for Unicom broadband users in the North | Optimal direct connection for Unicom users |
| 4837 | China Unicom Standard | Normal | Unicom standard international line, obvious congestion during evening peak but cheap | Backup/Cold Backup/Budget-sensitive |
How to Select Nodes and Protocols
Do not hardcode vendors, package prices, or specific speed test results in the architecture document: this information changes quickly and cannot represent the experience of different regions, operators, and evening peak periods. A more robust selection method is to continuously measure using your own access network:
- Record RTT, jitter, packet loss rate, and TCP/UDP throughput separately during weekdays and evening peaks.
- Choose different upstreams or different routes for primary and backup nodes to avoid the scenario where multiple nodes appear to be independent but actually share the same fault domain.
- Test both TCP and UDP protocols; protocol performance depends on link quality, and long-term conclusions cannot be derived from a single latency test.
- Switching strategies should consider business experience. Automatic switching is suitable for scenarios where node quality is similar; when quality differences are large, manual switching makes it easier to detect degradation.
- Feed raw results to Prometheus Blackbox Exporter or similar probes for storage, and make decisions based on percentiles over a week or more rather than instantaneous minimum values.
This network chooses a manual Selector: alert when the primary node is abnormal, and switch after confirmation by the maintainer. The cost is that recovery is not fully automatic, but the benefit is that it won't silently drop to a node that "can connect but has a poor experience."
Speed-Up Items
tcp-concurrent: true: Concurrent dial-up resolves multiple IPs and picks the fastest.- QUIC reject rule intentionally retained:
AND,(DST-PORT,443),(NETWORK,UDP),(NOT,GEOSITE,cn),REJECTforces external HTTP/3→h2, curing "QUIC-over-Hy2 idle reuse hang"; enabling it will cause recurrence.
Why not enable hard bypass for IPv6: The key to v4 hard bypass is hairpin MASQUERADE (changing source to .7 to ensure symmetric return), which relies on N100 having a fixed internal v4 address. However, v6 addresses are dynamically assigned via SLAAC, with no stable internal address to serve as an SNAT anchor — symmetric return for hairpin cannot be reliably achieved on v6; forcing it will only cause random disconnections. Therefore, v6 CN currently only relies on the mihomo ruleset layer
GEOIP,CNfor fallback, not bypassing via firewall prerouting. Monitoring: Key components of N100, core workstation, and main router are integrated into Prometheus + Grafana, see monitoring.md. Node availability is probed by Prometheus Blackbox Exporter; the manual confirmation switching strategy is retained here.
7. N100 Key Components
| Component | Address/Path | Description |
|---|---|---|
| mihomo Kernel | /usr/bin/mihomo, procd via /etc/init.d/nikki | Transparent Proxy + TUN |
| mihomo API | 127.0.0.1:9090 | Health Probe |
| mihomo Mixed | :7890 (SOCKS5+HTTP) | Explicit Proxy Entry |
| mihomo Redir | :7891 | Transparent Proxy Redirect Inbound |
| mihomo DNS | :1053 (fake-ip) | AdGuard Upstream |
| Nikki Dashboard | http://<side-router-ip>:9090/ui | Web Console, Intranet Access Only |
| AdGuard Home | Docker host network, http://<side-router-ip>:8083 | DNS Ad Filtering, Upstream points to :1053 |
| failopen Daemon | /usr/local/sbin/nikki-failopen.sh, procd | Health Check + Auto Fail-Open |
| CN Bypass SNAT | /etc/nikki/scripts/bypass-masq.{nft,sh} + firewall include | Hairpin masquerade, Persistent |
N100 Migration Motivation and Hardware Selection: The original side router ran on a desktop PC via WiFi; WiFi jitter caused whole-house outages. Switched to N100 (low-power x86, iStoreOS, single Gigabit port) hanging on the Study AP LAN port as a wired side router, decoupling from WiFi henceforth.
8. nftables Structure
Processing Order (prerouting → forward → postrouting):
table inet fw4 (OpenWrt Firewall)
forward: policy drop; allow TUN traffic + LAN→WAN forwardingsrcnat_lan: docker MASQUERADE
table inet nikki (Transparent Proxy Rules)
china_ipset: ~3.7k chnroute CIDRs (filled bybypass_china_mainland_ip=1)dstnat/mangle_prerouting_lan: Mainland IP / Private / 198.18 match →returnbypass; others DNAT TCP→:7891 or Mark TUNmangle_output: Local outbound → TUN- Entire table deleted during fail-open
table inet bypass_masq (Hairpin SNAT for CN-IP Bypass)
postrouting(nat hook, priority srcnat+5):iifname br-lan oifname br-lan ip daddr != {private+198.18+multicast} masquerade- Only matches public CN direct traffic bypassed by china_ip that needs forwarding back to .1, changing source to .7 to ensure symmetric return
- Independent of table nikki, nikki restart does not affect it; persistence relies on
firewall.bypass_masqinclude
table inet failopen (Fault Injection, Deleted on Recovery)
srcnat: MASQUERADE<lan-cidr>→ non-localdns: DNAT :53 → .1:53
9. Self-Hosted Services and Fault Domains
The following services currently rely on the core workstation's Wi-Fi. wifi-watchdog (systemd timer) can recover brief RF or driver anomalies, but it cannot turn a wireless link into a high-availability infrastructure; long-running entry and monitoring services should be migrated to wired nodes.
| Service | Purpose | Availability |
|---|---|---|
| DERP | Tailscale Relay | High |
| rathole | Intranet Tunnel (see rathole-tunnel.md) | High |
| paste/mdserve | Note Sharing | Medium |
| Prometheus | Monitoring Collection | Medium |
| sunshine | Game Streaming | Low |
| Local LLM | AI Inference (see local-llm.md) | Low |
10. Daemons
| Daemon | Location | Scenario |
|---|---|---|
| nikki-failopen | N100 (procd) | mihomo/AdGuard process abnormal |
| Power-On Self-Start | N100 (BIOS) | Power Recovery |
| wifi-watchdog | Core Workstation (systemd timer) | Wi-Fi Driver/RF Anomaly |
| sync-mihomo-to-router | Core Workstation (systemd timer) | Source → Side Router Config Drift |
| sync-subscribe | Core Workstation (systemd timer) | Source → Mobile Config Drift |
Configuration Derivation Pipeline
There are three places in the home that need mihomo configuration: Core Workstation (TUN self-proxy), N100 (Whole-house transparent proxy), and Mobile Import File. The node list, proxy groups, and splitting rules of the three must be consistent, but DNS, TUN, and inbound methods are all different. If maintained independently, drift is inevitable.
Solution: Manually edit <config-root>/config.yaml only, which contains proxies, proxy-groups, and rules; two timer-driven scripts derive target configurations from it.
#1 Local Machine: Use the source directly. TUN device Meta + Local AdGuard + fake-ip + controller.
#2 N100 Side Router (sync-mihomo-to-router.sh, timer 03:45):
- Extract only
proxies/proxy-groups/rulessegments from the source →/etc/nikki/profiles/home.yaml - Validate source with
mihomo -tlocally → scp to N100 →nikki -tvalidate → Backup old file (with timestamp) → Auto rollback on validation failure → Reload Nikki - DNS/TUN/sniffer are injected by N100 local UCI mixin, not entering the profile — these are target-end specific and should not be overwritten from the source
#3 Mobile Configuration (sync-subscribe.sh):
- Output
clash.yaml(Clash Import) +sub.txt(v2rayN base64 share link) - De-localization sanitization: Remove
external-controller+secret(leak means controlled),tunentire section (phones don't need it),nameserver:127.0.0.1(roaming devices can't reach),PROCESS-NAMErules (phones never match) - DNS replaced with fake-ip + public DoH, international domains resolved via exit to prevent leakage
- If distributed via public network, the entry uses an independent domain and unpredictable path token; the Web server only exposes derived files, not the source, controller secret, or directory listing
The two timers run staggered to avoid reading or writing target configurations simultaneously. After modifying the source, you can also manually execute the corresponding script; scripts must remain idempotent and complete syntax validation and backup before replacement.
11. Post-Launch Verification Checklist
Put the side router address into the environment variable first to avoid exposing real addresses in example commands:
SIDE_ROUTER_IP=192.168.31.7
At least verify the following items in normal mode:
# Whether the client's default route points to the side router
# Whether DNS only passes through AdGuard and gets the expected fake-ip response
# Confirm forwarding, redirect, and SNAT rules exist on the side router
# Whether the two processes and their listening ports are alive
Fail-Open should be rehearsed during maintenance windows, not guessed after a failure occurs. Stop mihomo and AdGuard Home separately, confirm that the daemon revokes transparent proxy rules, takes over DNS, and clients can still connect directly; then restore services and confirm that rules are not injected repeatedly. Finally, simulate N100 power loss separately to verify that monitoring indeed alarms, while accepting the fact that ordinary clients will lose connectivity under the current architecture.
| Scenario | Expected Result |
|---|---|
| mihomo process exits | Auto-revoke redirect, degrade to direct connection |
| AdGuard Home process exits | :53 taken over by available DNS, clients continue resolving |
| N100 restarts and recovers | Configuration injected only once, routing and DNS return to normal mode |
| N100 power loss or port disconnect | No gateway hot standby currently, ordinary clients lose connectivity and trigger alarm |
| Core Workstation offline | Ordinary home devices unaffected; self-hosted services offline |
12. Known Risks
| Risk | Impact | Mitigation |
|---|---|---|
| Core Workstation WiFi Single Point | Self-hosted services like tunnels and relays go offline simultaneously | Long-term migration to wired nodes; watchdog is only a short-term band-aid |
| N100 Single Port Hairpin | Asymmetric return path in single-arm topology must break | MASQUERADE + bypass_masq nft table ensures symmetric return |
| mihomo/AdGuard Process Hang | Whole-house outage (DHCP gateway still points to .7) | nikki-failopen (procd) process-level self-healing |
| N100 Hardware, Power, or Link Failure | Default gateway unreachable, ordinary clients lose connectivity | Currently only monitoring alarms; introduce double-active gateway or main router side auto-switching later |
| Main Router Hardware/Firmware Failure | Whole-house outage (PPPoE+NAT+DHCP all down) | No hot standby currently; original plan for N100 as main router not implemented |
| Optical Modem Bridging | ONT only does optical-electrical conversion, no dial-up/routing/NAT, public IP goes directly to main router | Basically not a failure source, no double NAT |
Detailed evaluation and recovery procedures see network-recovery.md.
Change Log
| Date | Change |
|---|---|
| 2026-06-21 | N100 side router online, old workstation side router retired; DHCP gateway/DNS switched from .8 to .7; Deployed Fail-Open and Wi-Fi watchdog |
| 2026-06-24 | Public subscription fix: Original config copy caused leakage of local machine DNS + management port secret + tun, sync-subscribe.sh added de-localization sanitization; Side router profile rerun refreshed; Added sync-mihomo-to-router.timer |
| 2026-06-27 | Disabled ICMP send_redirects: Cured Xianyu and other App freezes (root cause of dual-path PAWS packet loss storm); CN-IP firewall bypass online (bypass_china_mainland_ip=1 + table inet bypass_masq); tcp-concurrent + CN DNS changed to plaintext UDP 223.5.5.5; Node insights |
| 2026-08-11 | Public version document generalized hosts, SSIDs, subscription entries, and node info; Added design principles and verification methods |
Related Documents
- rathole-tunnel.md — Tunnel-to-Home Solution
- network-recovery.md — Failure Recovery Manual
- monitoring.md — Monitoring Architecture